From 814be66b98f9bd09a31cc8298633cc230c74d86b Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 26 Apr 2025 16:05:07 +0530 Subject: [PATCH 001/173] update: use new terminologies on path params and local variables. --- app/controllers/api/databases.php | 1806 +++++++++++++++-------------- 1 file changed, 909 insertions(+), 897 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index c5d0e9e754..d3a34f56e8 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -66,11 +66,11 @@ use Utopia\Validator\URL; use Utopia\Validator\WhiteList; /** - * * Create attribute of varying type + * * Create column of varying type * * @param string $databaseId - * @param string $collectionId - * @param Document $attribute + * @param string $tableId + * @param Document $column * @param Response $response * @param Database $dbForProject * @param EventDatabase $queueForDatabase @@ -85,19 +85,19 @@ use Utopia\Validator\WhiteList; * @throws ConflictException * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document +function createColumn(string $databaseId, string $tableId, Document $column, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document { - $key = $attribute->getAttribute('key'); - $type = $attribute->getAttribute('type', ''); - $size = $attribute->getAttribute('size', 0); - $required = $attribute->getAttribute('required', true); - $signed = $attribute->getAttribute('signed', true); // integers are signed by default - $array = $attribute->getAttribute('array', false); - $format = $attribute->getAttribute('format', ''); - $formatOptions = $attribute->getAttribute('formatOptions', []); - $filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint - $default = $attribute->getAttribute('default'); - $options = $attribute->getAttribute('options', []); + $key = $column->getAttribute('key'); + $type = $column->getAttribute('type', ''); + $size = $column->getAttribute('size', 0); + $required = $column->getAttribute('required', true); + $signed = $column->getAttribute('signed', true); // integers are signed by default + $array = $column->getAttribute('array', false); + $format = $column->getAttribute('format', ''); + $formatOptions = $column->getAttribute('formatOptions', []); + $filters = $column->getAttribute('filters', []); // filters are hidden from the endpoint + $default = $column->getAttribute('default'); + $options = $column->getAttribute('options', []); $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -105,43 +105,43 @@ function createAttribute(string $databaseId, string $collectionId, Document $att throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } if (!empty($format)) { if (!Structure::hasFormat($format, $type)) { - throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} attributes."); + throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns."); } } // Must throw here since dbForProject->createAttribute is performed by db worker if ($required && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute'); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); } if ($array && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); } if ($type === Database::VAR_RELATIONSHIP) { $options['side'] = Database::RELATION_SIDE_PARENT; - $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); - if ($relatedCollection->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related collection was not found.'); + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); + if ($relatedTable->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related table was not found.'); } } try { - $attribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + $column = new Document([ + '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), 'key' => $key, 'databaseInternalId' => $db->getInternalId(), 'databaseId' => $db->getId(), - 'collectionInternalId' => $collection->getInternalId(), - 'collectionId' => $collectionId, + 'collectionInternalId' => $table->getInternalId(), + 'collectionId' => $tableId, 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck 'size' => $size, @@ -155,35 +155,35 @@ function createAttribute(string $databaseId, string $collectionId, Document $att 'options' => $options, ]); - $dbForProject->checkAttribute($collection, $attribute); - $attribute = $dbForProject->createDocument('attributes', $attribute); + $dbForProject->checkAttribute($table, $column); + $column = $dbForProject->createDocument('attributes', $column); } catch (DuplicateException) { throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); } catch (LimitException) { throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); } catch (\Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); throw $e; } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { $twoWayKey = $options['twoWayKey']; - $options['relatedCollection'] = $collection->getId(); + $options['relatedCollection'] = $table->getId(); $options['twoWayKey'] = $key; $options['side'] = Database::RELATION_SIDE_CHILD; try { $twoWayAttribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $twoWayKey), + '$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey), 'key' => $twoWayKey, 'databaseInternalId' => $db->getInternalId(), 'databaseId' => $db->getId(), - 'collectionInternalId' => $relatedCollection->getInternalId(), - 'collectionId' => $relatedCollection->getId(), + 'collectionInternalId' => $relatedTable->getInternalId(), + 'collectionId' => $relatedTable->getId(), 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck 'size' => $size, @@ -197,45 +197,45 @@ function createAttribute(string $databaseId, string $collectionId, Document $att 'options' => $options, ]); - $dbForProject->checkAttribute($relatedCollection, $twoWayAttribute); + $dbForProject->checkAttribute($relatedTable, $twoWayAttribute); $dbForProject->createDocument('attributes', $twoWayAttribute); } catch (DuplicateException) { - $dbForProject->deleteDocument('attributes', $attribute->getId()); + $dbForProject->deleteDocument('attributes', $column->getId()); throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); } catch (LimitException) { - $dbForProject->deleteDocument('attributes', $attribute->getId()); + $dbForProject->deleteDocument('attributes', $column->getId()); throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); } catch (\Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); throw $e; } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); } $queueForDatabase ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) ->setDatabase($db) - ->setCollection($collection) - ->setDocument($attribute); + ->setCollection($table) + ->setDocument($column); $queueForEvents - ->setContext('collection', $collection) + ->setContext('table', $table) ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('attributeId', $attribute->getId()); + ->setParam('tableId', $table->getId()) + ->setParam('columnId', $column->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); - return $attribute; + return $column; } -function updateAttribute( +function updateColumn( string $databaseId, - string $collectionId, + string $tableId, string $key, Database $dbForProject, Event $queueForEvents, @@ -256,59 +256,59 @@ function updateAttribute( throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } - if ($attribute->getAttribute('status') !== 'available') { + if ($column->getAttribute('status') !== 'available') { throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE); } - if ($attribute->getAttribute(('type') !== $type)) { + if ($column->getAttribute(('type') !== $type)) { throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); } - if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) { + if ($column->getAttribute('type') === Database::VAR_STRING && $column->getAttribute(('filter') !== $filter)) { throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); } if ($required && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute'); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); } - if ($attribute->getAttribute('array', false) && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); + if ($column->getAttribute('array', false) && isset($default)) { + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); } - $collectionId = 'database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId(); + $tableId = 'database_' . $db->getInternalId() . '_collection_' . $table->getInternalId(); - $attribute + $column ->setAttribute('default', $default) ->setAttribute('required', $required); if (!empty($size)) { - $attribute->setAttribute('size', $size); + $column->setAttribute('size', $size); } - switch ($attribute->getAttribute('format')) { + switch ($column->getAttribute('format')) { case APP_DATABASE_ATTRIBUTE_INT_RANGE: case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE: - $min ??= $attribute->getAttribute('formatOptions')['min']; - $max ??= $attribute->getAttribute('formatOptions')['max']; + $min ??= $column->getAttribute('formatOptions')['min']; + $max ??= $column->getAttribute('formatOptions')['max']; if ($min > $max) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } - if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { + if ($column->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { $validator = new Range($min, $max, Database::VAR_INTEGER); } else { $validator = new Range($min, $max, Database::VAR_FLOAT); @@ -326,7 +326,7 @@ function updateAttribute( 'min' => $min, 'max' => $max ]; - $attribute->setAttribute('formatOptions', $options); + $column->setAttribute('formatOptions', $options); break; case APP_DATABASE_ATTRIBUTE_ENUM: @@ -348,44 +348,44 @@ function updateAttribute( 'elements' => $elements ]; - $attribute->setAttribute('formatOptions', $options); + $column->setAttribute('formatOptions', $options); break; } if ($type === Database::VAR_RELATIONSHIP) { - $primaryDocumentOptions = \array_merge($attribute->getAttribute('options', []), $options); - $attribute->setAttribute('options', $primaryDocumentOptions); + $primaryRowOptions = \array_merge($column->getAttribute('options', []), $options); + $column->setAttribute('options', $primaryRowOptions); try { $dbForProject->updateRelationship( - collection: $collectionId, + collection: $tableId, id: $key, newKey: $newKey, - onDelete: $primaryDocumentOptions['onDelete'], + onDelete: $primaryRowOptions['onDelete'], ); } catch (NotFoundException) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } - if ($primaryDocumentOptions['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']); + if ($primaryRowOptions['twoWay']) { + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryRowOptions['relatedCollection']); - $relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']); + $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey']); if (!empty($newKey) && $newKey !== $key) { $options['twoWayKey'] = $newKey; } - $relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options); - $relatedAttribute->setAttribute('options', $relatedOptions); - $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute); + $relatedOptions = \array_merge($relatedColumn->getAttribute('options'), $options); + $relatedColumn->setAttribute('options', $relatedOptions); + $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey'], $relatedColumn); - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); } } else { try { $dbForProject->updateAttribute( - collection: $collectionId, + collection: $tableId, id: $key, size: $size, required: $required, @@ -405,44 +405,44 @@ function updateAttribute( } if (!empty($newKey) && $key !== $newKey) { - $originalUid = $attribute->getId(); + $originalUid = $column->getId(); - $attribute - ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey)) + $column + ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $newKey)) ->setAttribute('key', $newKey); - $dbForProject->updateDocument('attributes', $originalUid, $attribute); + $dbForProject->updateDocument('attributes', $originalUid, $column); /** * @var Document $index */ - foreach ($collection->getAttribute('indexes') as $index) { + foreach ($table->getAttribute('indexes') as $index) { /** - * @var string[] $attributes + * @var string[] $columns */ - $attributes = $index->getAttribute('attributes', []); - $found = \array_search($key, $attributes); + $columns = $index->getAttribute('attributes', []); + $found = \array_search($key, $columns); if ($found !== false) { - $attributes[$found] = $newKey; - $index->setAttribute('attributes', $attributes); + $columns[$found] = $newKey; + $index->setAttribute('attributes', $columns); $dbForProject->updateDocument('indexes', $index->getId(), $index); } } } else { - $attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); + $column = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key, $column); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $table->getId()); $queueForEvents - ->setContext('collection', $collection) + ->setContext('table', $table) ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('attributeId', $attribute->getId()); + ->setParam('tableId', $table->getId()) + ->setParam('columnId', $column->getId()); - return $attribute; + return $column; } App::init() @@ -845,18 +845,19 @@ App::delete('/v1/databases/:databaseId') $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections') - ->desc('Create collection') +App::post('/v1/databases/:databaseId/tables') + ->alias('/v1/databases/:databaseId/collections') + ->desc('Create table') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'collection.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}') + ->label('audits.event', 'table.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') ->label('sdk', new Method( namespace: 'databases', - group: 'collections', - name: 'createCollection', + group: 'tables', + name: 'createTable', description: '/docs/references/databases/create-collection.md', auth: [AuthType::KEY], responses: [ @@ -868,8 +869,8 @@ App::post('/v1/databases/:databaseId/collections') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') + ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) @@ -877,7 +878,7 @@ App::post('/v1/databases/:databaseId/collections') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -885,24 +886,24 @@ App::post('/v1/databases/:databaseId/collections') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collectionId = $collectionId == 'unique()' ? ID::unique() : $collectionId; + $tableId = $tableId == 'unique()' ? ID::unique() : $tableId; // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions) ?? []; try { - $collection = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ - '$id' => $collectionId, + $table = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ + '$id' => $tableId, 'databaseInternalId' => $database->getInternalId(), 'databaseId' => $databaseId, '$permissions' => $permissions, 'documentSecurity' => $documentSecurity, 'enabled' => $enabled, 'name' => $name, - 'search' => implode(' ', [$collectionId, $name]), + 'search' => implode(' ', [$tableId, $name]), ])); - $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); + $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); } catch (DuplicateException) { throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); } catch (LimitException) { @@ -912,23 +913,23 @@ App::post('/v1/databases/:databaseId/collections') $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()); + ->setParam('tableId', $table->getId()); $response ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($collection, Response::MODEL_COLLECTION); + ->dynamic($table, Response::MODEL_COLLECTION); }); -App::get('/v1/databases/:databaseId/collections') - ->alias('/v1/database/collections') - ->desc('List collections') +App::get('/v1/databases/:databaseId/tables') + ->alias('/v1/databases/:databaseId/collections') + ->desc('List tables') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'collections', - name: 'listCollections', + group: 'tables', + name: 'listTables', description: '/docs/references/databases/list-collections.md', auth: [AuthType::KEY], responses: [ @@ -974,11 +975,11 @@ App::get('/v1/databases/:databaseId/collections') throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $collectionId = $cursor->getValue(); - $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $tableId = $cursor->getValue(); + $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($cursorDocument->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$collectionId}' for the 'cursor' value not found."); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Table '{$tableId}' for the 'cursor' value not found."); } $cursor->setValue($cursorDocument); @@ -987,27 +988,29 @@ App::get('/v1/databases/:databaseId/collections') $filterQueries = Query::groupByType($queries)['filters']; try { - $collections = $dbForProject->find('database_' . $database->getInternalId(), $queries); + $tables = $dbForProject->find('database_' . $database->getInternalId(), $queries); $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + + // TODO: collections > tables $response->dynamic(new Document([ - 'collections' => $collections, + 'collections' => $tables, 'total' => $total, ]), Response::MODEL_COLLECTION_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId') - ->alias('/v1/database/collections/:collectionId') - ->desc('Get collection') +App::get('/v1/databases/:databaseId/tables/:tableId') + ->alias('/v1/databases/:databaseId/collections/:tableId') + ->desc('Get table') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'collections', - name: 'getCollection', + group: 'tables', + name: 'getTable', description: '/docs/references/databases/get-collection.md', auth: [AuthType::KEY], responses: [ @@ -1019,11 +1022,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('tableId', '', new UID(), 'Table ID.') ->inject('response') ->inject('dbForProject') - ->inject('mode') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode) { + ->action(function (string $databaseId, string $tableId, Response $response, Database $dbForProject) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1031,25 +1033,25 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $response->dynamic($collection, Response::MODEL_COLLECTION); + $response->dynamic($table, Response::MODEL_COLLECTION); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/logs') - ->alias('/v1/database/collections/:collectionId/logs') - ->desc('List collection logs') +App::get('/v1/databases/:databaseId/tables/:tableId/logs') + ->alias('/v1/databases/:databaseId/collections/:tableId/logs') + ->desc('List table logs') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'collections', - name: 'listCollectionLogs', + group: 'tables', + name: 'listTableLogs', description: '/docs/references/databases/get-collection-logs.md', auth: [AuthType::ADMIN], responses: [ @@ -1061,13 +1063,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('tableId', '', new UID(), 'Table ID.') ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) ->inject('response') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1075,10 +1077,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -1095,7 +1097,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') ]); $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/collection/' . $collectionId; + $resource = 'database/' . $databaseId . '/table/' . $tableId; $logs = $audit->getLogsByResource($resource, $queries); $output = []; @@ -1150,19 +1152,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') }); -App::put('/v1/databases/:databaseId/collections/:collectionId') - ->alias('/v1/database/collections/:collectionId') - ->desc('Update collection') +App::put('/v1/databases/:databaseId/tables/:tableId') + ->alias('/v1/databases/:databaseId/collections/:tableId') + ->desc('Update table') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].update') - ->label('audits.event', 'collection.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].update') + ->label('audits.event', 'table.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'collections', - name: 'updateCollection', + group: 'tables', + name: 'updateTable', description: '/docs/references/databases/update-collection.md', auth: [AuthType::KEY], responses: [ @@ -1174,7 +1176,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('tableId', '', new UID(), 'Table ID.') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) @@ -1183,7 +1185,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1191,53 +1193,53 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $permissions ??= $collection->getPermissions() ?? []; + $permissions ??= $table->getPermissions() ?? []; // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions); - $enabled ??= $collection->getAttribute('enabled', true); + $enabled ??= $table->getAttribute('enabled', true); - $collection = $dbForProject->updateDocument( + $table = $dbForProject->updateDocument( 'database_' . $database->getInternalId(), - $collectionId, - $collection + $tableId, + $table ->setAttribute('name', $name) ->setAttribute('$permissions', $permissions) ->setAttribute('documentSecurity', $documentSecurity) ->setAttribute('enabled', $enabled) - ->setAttribute('search', \implode(' ', [$collectionId, $name])) + ->setAttribute('search', \implode(' ', [$tableId, $name])) ); - $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity); + $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $permissions, $documentSecurity); $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()); + ->setParam('tableId', $table->getId()); - $response->dynamic($collection, Response::MODEL_COLLECTION); + $response->dynamic($table, Response::MODEL_COLLECTION); }); -App::delete('/v1/databases/:databaseId/collections/:collectionId') - ->alias('/v1/database/collections/:collectionId') - ->desc('Delete collection') +App::delete('/v1/databases/:databaseId/tables/:tableId') + ->alias('/v1/databases/:databaseId/collections/:tableId') + ->desc('Delete table') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].delete') - ->label('audits.event', 'collection.delete') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].delete') + ->label('audits.event', 'table.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'collections', - name: 'deleteCollection', + group: 'tables', + name: 'deleteTable', description: '/docs/references/databases/delete-collection.md', auth: [AuthType::KEY], responses: [ @@ -1249,13 +1251,13 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('tableId', '', new UID(), 'Table ID.') ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { + ->action(function (string $databaseId, string $tableId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1263,45 +1265,45 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $collectionId)) { + if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $tableId)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); } - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_COLLECTION) ->setDatabase($database) - ->setCollection($collection); + ->setCollection($table); $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setPayload($response->output($collection, Response::MODEL_COLLECTION)); + ->setParam('tableId', $table->getId()) + ->setPayload($response->output($table, Response::MODEL_COLLECTION)); $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string') - ->alias('/v1/database/collections/:collectionId/attributes/string') - ->desc('Create string attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/string') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/string') + ->desc('Create string column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createStringAttribute', + group: 'columns', + name: 'createStringColumn', description: '/docs/references/databases/create-string-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1312,18 +1314,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) - ->param('encrypt', false, new Boolean(), 'Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.', true) + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Text(0, 0), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within required size $validator = new Text($size, 0); @@ -1337,7 +1339,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string $filters[] = 'encrypt'; } - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_STRING, 'size' => $size, @@ -1349,22 +1351,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); + ->dynamic($column, Response::MODEL_ATTRIBUTE_STRING); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email') - ->alias('/v1/database/collections/:collectionId/attributes/email') - ->desc('Create email attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/email') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/email') + ->desc('Create email column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createEmailAttribute', + group: 'columns', + name: 'createEmailColumn', description: '/docs/references/databases/create-email-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1375,18 +1377,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Email(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_STRING, 'size' => 254, @@ -1398,22 +1400,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); + ->dynamic($column, Response::MODEL_ATTRIBUTE_EMAIL); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') - ->alias('/v1/database/collections/:collectionId/attributes/enum') - ->desc('Create enum attribute') +App::post('/v1/databases/:databaseId/collections/:tableId/attributes/enum') + ->alias('/v1/database/collections/:tableId/attributes/enum') + ->desc('Create enum column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createEnumAttribute', + group: 'columns', + name: 'createEnumColumn', description: '/docs/references/databases/create-attribute-enum.md', auth: [AuthType::KEY], responses: [ @@ -1424,22 +1426,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') ->param('elements', [], new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Text(0), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { if (!is_null($default) && !in_array($default, $elements)) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); } - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_STRING, 'size' => Database::LENGTH_KEY, @@ -1452,22 +1454,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); + ->dynamic($column, Response::MODEL_ATTRIBUTE_ENUM); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') - ->alias('/v1/database/collections/:collectionId/attributes/ip') - ->desc('Create IP address attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/ip') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/ip') + ->desc('Create IP address column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createIpAttribute', + group: 'columns', + name: 'createIpColumn', description: '/docs/references/databases/create-ip-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1478,18 +1480,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new IP(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new IP(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_STRING, 'size' => 39, @@ -1501,22 +1503,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); + ->dynamic($column, Response::MODEL_ATTRIBUTE_IP); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') - ->alias('/v1/database/collections/:collectionId/attributes/url') - ->desc('Create URL attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/url') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/url') + ->desc('Create URL column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createUrlAttribute', + group: 'columns', + name: 'createUrlColumn', description: '/docs/references/databases/create-url-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1527,18 +1529,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new URL(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_STRING, 'size' => 2000, @@ -1550,22 +1552,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); + ->dynamic($column, Response::MODEL_ATTRIBUTE_URL); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') - ->alias('/v1/database/collections/:collectionId/attributes/integer') - ->desc('Create integer attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/integer') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/integer') + ->desc('Create integer column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createIntegerAttribute', + group: 'columns', + name: 'createIntegerColumn', description: '/docs/references/databases/create-integer-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1576,18 +1578,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') ->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true) ->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new Integer(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('default', null, new Integer(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within range $min ??= PHP_INT_MIN; @@ -1605,7 +1607,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege $size = $max > 2147483647 ? 8 : 4; // Automatically create BigInt depending on max value - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_INTEGER, 'size' => $size, @@ -1619,31 +1621,31 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ], ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - $formatOptions = $attribute->getAttribute('formatOptions', []); + $formatOptions = $column->getAttribute('formatOptions', []); if (!empty($formatOptions)) { - $attribute->setAttribute('min', \intval($formatOptions['min'])); - $attribute->setAttribute('max', \intval($formatOptions['max'])); + $column->setAttribute('min', \intval($formatOptions['min'])); + $column->setAttribute('max', \intval($formatOptions['max'])); } $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); + ->dynamic($column, Response::MODEL_ATTRIBUTE_INTEGER); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float') - ->alias('/v1/database/collections/:collectionId/attributes/float') - ->desc('Create float attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/float') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/float') + ->desc('Create float column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createFloatAttribute', + group: 'columns', + name: 'createFloatColumn', description: '/docs/references/databases/create-float-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1654,18 +1656,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true) ->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new FloatValidator(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('default', null, new FloatValidator(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within range $min ??= -PHP_FLOAT_MAX; @@ -1681,7 +1683,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_FLOAT, 'required' => $required, @@ -1695,31 +1697,31 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ], ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - $formatOptions = $attribute->getAttribute('formatOptions', []); + $formatOptions = $column->getAttribute('formatOptions', []); if (!empty($formatOptions)) { - $attribute->setAttribute('min', \floatval($formatOptions['min'])); - $attribute->setAttribute('max', \floatval($formatOptions['max'])); + $column->setAttribute('min', \floatval($formatOptions['min'])); + $column->setAttribute('max', \floatval($formatOptions['max'])); } $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); + ->dynamic($column, Response::MODEL_ATTRIBUTE_FLOAT); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') - ->alias('/v1/database/collections/:collectionId/attributes/boolean') - ->desc('Create boolean attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/boolean') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean') + ->desc('Create boolean column') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createBooleanAttribute', + group: 'columns', + name: 'createBooleanColumn', description: '/docs/references/databases/create-boolean-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1730,18 +1732,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Boolean(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Boolean(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_BOOLEAN, 'size' => 0, @@ -1752,22 +1754,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); + ->dynamic($column, Response::MODEL_ATTRIBUTE_BOOLEAN); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') - ->alias('/v1/database/collections/:collectionId/attributes/datetime') - ->desc('Create datetime attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/datetime') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime') + ->desc('Create datetime column') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createDatetimeAttribute', + group: 'columns', + name: 'createDatetimeColumn', description: '/docs/references/databases/create-datetime-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1778,20 +1780,20 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true, ['dbForProject']) - ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) + ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $filters[] = 'datetime'; - $attribute = createAttribute($databaseId, $collectionId, new Document([ + $column = createColumn($databaseId, $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_DATETIME, 'size' => 0, @@ -1803,22 +1805,22 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME); + ->dynamic($column, Response::MODEL_ATTRIBUTE_DATETIME); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') - ->alias('/v1/database/collections/:collectionId/attributes/relationship') - ->desc('Create relationship attribute') +App::post('/v1/databases/:databaseId/tables/:tableId/columns/relationship') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/relationship') + ->desc('Create relationship column') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'attribute.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'createRelationshipAttribute', + group: 'columns', + name: 'createRelationshipColumn', description: '/docs/references/databases/create-relationship-attribute.md', auth: [AuthType::KEY], responses: [ @@ -1829,12 +1831,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('relatedCollectionId', '', new UID(), 'Related Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('relatedTableId', '', new UID(), 'Related Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('type', '', new WhiteList([Database::RELATION_ONE_TO_ONE, Database::RELATION_MANY_TO_ONE, Database::RELATION_MANY_TO_MANY, Database::RELATION_ONE_TO_MANY], true), 'Relation type') ->param('twoWay', false, new Boolean(), 'Is Two Way?', true) - ->param('key', null, new Key(), 'Attribute Key.', true) - ->param('twoWayKey', null, new Key(), 'Two Way Attribute Key.', true) + ->param('key', null, new Key(), 'Column Key.', true) + ->param('twoWayKey', null, new Key(), 'Two Way Column Key.', true) ->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true) ->inject('response') ->inject('dbForProject') @@ -1842,8 +1844,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati ->inject('queueForEvents') ->action(function ( string $databaseId, - string $collectionId, - string $relatedCollectionId, + string $tableId, + string $relatedTableId, string $type, bool $twoWay, ?string $key, @@ -1854,8 +1856,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati EventDatabase $queueForDatabase, Event $queueForEvents ) { - $key ??= $relatedCollectionId; - $twoWayKey ??= $collectionId; + $key ??= $relatedTableId; + $twoWayKey ??= $tableId; $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1863,34 +1865,34 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId); - $relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId()); + $relatedTableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId); + $relatedTable = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedTableDocument->getInternalId()); - if ($relatedCollection->isEmpty()) { + if ($relatedTable->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attributes = $collection->getAttribute('attributes', []); - /** @var Document[] $attributes */ - foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') !== Database::VAR_RELATIONSHIP) { + $columns = $table->getAttribute('attributes', []); + /** @var Document[] $columns */ + foreach ($columns as $column) { + if ($column->getAttribute('type') !== Database::VAR_RELATIONSHIP) { continue; } - if (\strtolower($attribute->getId()) === \strtolower($key)) { + if (\strtolower($column->getId()) === \strtolower($key)) { throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); } if ( - \strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && - $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId() + \strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && + $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() ) { // Console should provide a unique twoWayKey input! throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); @@ -1898,16 +1900,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati if ( $type === Database::RELATION_MANY_TO_MANY && - $attribute->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && - $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId() + $column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && + $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() ) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same collection is currently not permitted.'); + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same table is currently not permitted.'); } } - $attribute = createAttribute( + $column = createColumn( $databaseId, - $collectionId, + $tableId, new Document([ 'key' => $key, 'type' => Database::VAR_RELATIONSHIP, @@ -1917,7 +1919,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati 'array' => false, 'filters' => [], 'options' => [ - 'relatedCollection' => $relatedCollectionId, + 'relatedCollection' => $relatedTableId, 'relationType' => $type, 'twoWay' => $twoWay, 'twoWayKey' => $twoWayKey, @@ -1930,27 +1932,27 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati $queueForEvents ); - $options = $attribute->getAttribute('options', []); + $options = $column->getAttribute('options', []); foreach ($options as $key => $option) { - $attribute->setAttribute($key, $option); + $column->setAttribute($key, $option); } $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_RELATIONSHIP); + ->dynamic($column, Response::MODEL_ATTRIBUTE_RELATIONSHIP); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') - ->alias('/v1/database/collections/:collectionId/attributes') - ->desc('List attributes') +App::get('/v1/databases/:databaseId/tables/:tableId/columns') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes') + ->desc('List columns') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'listAttributes', + group: 'columns', + name: 'listColumns', description: '/docs/references/databases/list-attributes.md', auth: [AuthType::KEY], responses: [ @@ -1961,11 +1963,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { + ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject) { /** @var Document $database */ $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1973,9 +1975,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -1984,7 +1986,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') \array_push( $queries, Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$collection->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]), ); /** @@ -2002,16 +2004,16 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $attributeId = $cursor->getValue(); + $columnId = $cursor->getValue(); $cursorDocument = Authorization::skip(fn () => $dbForProject->find('attributes', [ Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$collection->getInternalId()]), - Query::equal('key', [$attributeId]), + Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('key', [$columnId]), Query::limit(1), ])); if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Attribute '{$attributeId}' for the 'cursor' value not found."); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Column '{$columnId}' for the 'cursor' value not found."); } $cursor->setValue($cursorDocument[0]); @@ -2019,28 +2021,28 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') $filters = Query::groupByType($queries)['filters']; try { - $attributes = $dbForProject->find('attributes', $queries); + $columns = $dbForProject->find('attributes', $queries); $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); } $response->dynamic(new Document([ - 'attributes' => $attributes, + 'attributes' => $columns, 'total' => $total, ]), Response::MODEL_ATTRIBUTE_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') - ->alias('/v1/database/collections/:collectionId/attributes/:key') - ->desc('Get attribute') +App::get('/v1/databases/:databaseId/tables/:tableId/columns/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') + ->desc('Get column') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'getAttribute', + group: 'columns', + name: 'getColumn', description: '/docs/references/databases/get-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2062,11 +2064,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { + ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2074,25 +2076,25 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } // Select response model based on type and format - $type = $attribute->getAttribute('type'); - $format = $attribute->getAttribute('format'); - $options = $attribute->getAttribute('options', []); + $type = $column->getAttribute('type'); + $format = $column->getAttribute('format'); + $options = $column->getAttribute('options', []); foreach ($options as $key => $option) { - $attribute->setAttribute($key, $option); + $column->setAttribute($key, $option); } $model = match ($type) { @@ -2111,21 +2113,22 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') default => Response::MODEL_ATTRIBUTE, }; - $response->dynamic($attribute, $model); + $response->dynamic($column, $model); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') - ->desc('Update string attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/string/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/string/:key') + ->desc('Update string column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateStringAttribute', + group: 'columns', + name: 'updateStringColumn', description: '/docs/references/databases/update-string-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2137,20 +2140,20 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.') ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Maximum size of the string attribute.', true) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2163,21 +2166,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); + ->dynamic($column, Response::MODEL_ATTRIBUTE_STRING); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') - ->desc('Update email attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/email/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/email/:key') + ->desc('Update email column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateEmailAttribute', + group: 'columns', + name: 'updateEmailColumn', description: '/docs/references/databases/update-email-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2189,18 +2193,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Email()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2213,21 +2217,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); + ->dynamic($column, Response::MODEL_ATTRIBUTE_EMAIL); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') - ->desc('Update enum attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/enum/:key') + ->desc('Update enum column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateEnumAttribute', + group: 'columns', + name: 'updateEnumColumn', description: '/docs/references/databases/update-enum-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2239,19 +2244,19 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/ contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') ->param('elements', null, new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Text(0)), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2265,21 +2270,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/ $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); + ->dynamic($column, Response::MODEL_ATTRIBUTE_ENUM); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') - ->desc('Update IP address attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/ip/:key') + ->desc('Update IP address column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateIpAttribute', + group: 'columns', + name: 'updateIpColumn', description: '/docs/references/databases/update-ip-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2291,18 +2297,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Nullable(new IP()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new IP()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2315,21 +2321,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); + ->dynamic($column, Response::MODEL_ATTRIBUTE_IP); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') - ->desc('Update URL attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/url/:key') + ->desc('Update URL column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateUrlAttribute', + group: 'columns', + name: 'updateUrlColumn', description: '/docs/references/databases/update-url-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2341,18 +2348,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/: contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new URL()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2365,21 +2372,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/: $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); + ->dynamic($column, Response::MODEL_ATTRIBUTE_URL); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') - ->desc('Update integer attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/integer/:key') + ->desc('Update integer column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateIntegerAttribute', + group: 'columns', + name: 'updateIntegerColumn', description: '/docs/references/databases/update-integer-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2391,20 +2399,20 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') ->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true) ->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('default', null, new Nullable(new Integer()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2416,30 +2424,31 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ newKey: $newKey ); - $formatOptions = $attribute->getAttribute('formatOptions', []); + $formatOptions = $column->getAttribute('formatOptions', []); if (!empty($formatOptions)) { - $attribute->setAttribute('min', \intval($formatOptions['min'])); - $attribute->setAttribute('max', \intval($formatOptions['max'])); + $column->setAttribute('min', \intval($formatOptions['min'])); + $column->setAttribute('max', \intval($formatOptions['max'])); } $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); + ->dynamic($column, Response::MODEL_ATTRIBUTE_INTEGER); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') - ->desc('Update float attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/float/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/float/:key') + ->desc('Update float column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateFloatAttribute', + group: 'columns', + name: 'updateFloatColumn', description: '/docs/references/databases/update-float-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2451,20 +2460,20 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true) ->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('default', null, new Nullable(new FloatValidator()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2476,30 +2485,31 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float newKey: $newKey ); - $formatOptions = $attribute->getAttribute('formatOptions', []); + $formatOptions = $column->getAttribute('formatOptions', []); if (!empty($formatOptions)) { - $attribute->setAttribute('min', \floatval($formatOptions['min'])); - $attribute->setAttribute('max', \floatval($formatOptions['max'])); + $column->setAttribute('min', \floatval($formatOptions['min'])); + $column->setAttribute('max', \floatval($formatOptions['max'])); } $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); + ->dynamic($column, Response::MODEL_ATTRIBUTE_FLOAT); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') - ->desc('Update boolean attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean/:key') + ->desc('Update boolean column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateBooleanAttribute', + group: 'columns', + name: 'updateBooleanColumn', description: '/docs/references/databases/update-boolean-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2511,18 +2521,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2534,21 +2544,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); + ->dynamic($column, Response::MODEL_ATTRIBUTE_BOOLEAN); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') - ->desc('Update dateTime attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime/:key') + ->desc('Update dateTime column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateDatetimeAttribute', + group: 'columns', + name: 'updateDatetimeColumn', description: '/docs/references/databases/update-datetime-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2560,18 +2571,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') - ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for attribute when not provided. Cannot be set when attribute is required.', injections: ['dbForProject']) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for column when not provided. Cannot be set when column is required.', injections: ['dbForProject']) + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $attribute = updateAttribute( + ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + $column = updateColumn( databaseId: $databaseId, - collectionId: $collectionId, + tableId: $tableId, key: $key, dbForProject: $dbForProject, queueForEvents: $queueForEvents, @@ -2583,21 +2594,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME); + ->dynamic($column, Response::MODEL_ATTRIBUTE_DATETIME); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') - ->desc('Update relationship attribute') +App::patch('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/:key/relationship') + ->desc('Update relationship column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'updateRelationshipAttribute', + group: 'columns', + name: 'updateRelationshipColumn', description: '/docs/references/databases/update-relationship-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2609,16 +2621,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') ->param('onDelete', null, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->action(function ( string $databaseId, - string $collectionId, + string $tableId, string $key, ?string $onDelete, ?string $newKey, @@ -2626,9 +2638,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ Database $dbForProject, Event $queueForEvents ) { - $attribute = updateAttribute( + $column = updateColumn( $databaseId, - $collectionId, + $tableId, $key, $dbForProject, $queueForEvents, @@ -2640,30 +2652,30 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ newKey: $newKey ); - $options = $attribute->getAttribute('options', []); + $options = $column->getAttribute('options', []); foreach ($options as $key => $option) { - $attribute->setAttribute($key, $option); + $column->setAttribute($key, $option); } $response ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($attribute, Response::MODEL_ATTRIBUTE_RELATIONSHIP); + ->dynamic($column, Response::MODEL_ATTRIBUTE_RELATIONSHIP); }); -App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') - ->alias('/v1/database/collections/:collectionId/attributes/:key') - ->desc('Delete attribute') +App::delete('/v1/databases/:databaseId/tables/:tableId/columns/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') + ->desc('Delete column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') - ->label('audits.event', 'attribute.delete') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'deleteAttribute', + group: 'columns', + name: 'deleteColumn', description: '/docs/references/databases/delete-attribute.md', auth: [AuthType::KEY], responses: [ @@ -2675,29 +2687,29 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Attribute Key.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } @@ -2705,55 +2717,55 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key * Check index dependency */ $validator = new IndexDependencyValidator( - $collection->getAttribute('indexes'), + $table->getAttribute('indexes'), $dbForProject->getAdapter()->getSupportForCastIndexArray(), ); - if (! $validator->isValid($attribute)) { + if (! $validator->isValid($column)) { throw new Exception(Exception::INDEX_DEPENDENCY); } // Only update status if removing available attribute - if ($attribute->getAttribute('status') === 'available') { - $attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting')); + if ($column->getAttribute('status') === 'available') { + $column = $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); + if ($column->getAttribute('type') === Database::VAR_RELATIONSHIP) { + $options = $column->getAttribute('options'); if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { + if ($relatedTable->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); - if ($relatedAttribute->isEmpty()) { + if ($relatedColumn->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } - if ($relatedAttribute->getAttribute('status') === 'available') { - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'deleting')); + if ($relatedColumn->getAttribute('status') === 'available') { + $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'deleting')); } $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); } } $queueForDatabase ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setCollection($collection) + ->setCollection($table) ->setDatabase($db) - ->setDocument($attribute); + ->setDocument($column); // Select response model based on type and format - $type = $attribute->getAttribute('type'); - $format = $attribute->getAttribute('format'); + $type = $column->getAttribute('type'); + $format = $column->getAttribute('format'); $model = match ($type) { Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, @@ -2773,27 +2785,27 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $queueForEvents ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('attributeId', $attribute->getId()) - ->setContext('collection', $collection) + ->setParam('tableId', $table->getId()) + ->setParam('columnId', $column->getId()) + ->setContext('table', $table) ->setContext('database', $db) - ->setPayload($response->output($attribute, $model)); + ->setPayload($response->output($column, $model)); $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') - ->alias('/v1/database/collections/:collectionId/indexes') +App::post('/v1/databases/:databaseId/tables/:tableId/indexes') + ->alias('/v1/databases/:databaseId/collections/:tableId/indexes') ->desc('Create index') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'index.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'collections', + group: 'tables', name: 'createIndex', description: '/docs/references/databases/create-index.md', auth: [AuthType::KEY], @@ -2806,16 +2818,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', null, new Key(), 'Index Key.') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') - ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') + ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2823,14 +2835,14 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } $count = $dbForProject->count('indexes', [ - Query::equal('collectionInternalId', [$collection->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]), Query::equal('databaseInternalId', [$db->getInternalId()]) ], 61); @@ -2841,9 +2853,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') } // Convert Document[] to array of attribute metadata - $oldAttributes = \array_map(fn ($a) => $a->getArrayCopy(), $collection->getAttribute('attributes')); + $oldColumns = \array_map(fn ($a) => $a->getArrayCopy(), $table->getAttribute('attributes')); - $oldAttributes[] = [ + $oldColumns[] = [ 'key' => '$id', 'type' => Database::VAR_STRING, 'status' => 'available', @@ -2853,7 +2865,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'size' => Database::LENGTH_KEY ]; - $oldAttributes[] = [ + $oldColumns[] = [ 'key' => '$createdAt', 'type' => Database::VAR_DATETIME, 'status' => 'available', @@ -2864,7 +2876,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'size' => 0 ]; - $oldAttributes[] = [ + $oldColumns[] = [ 'key' => '$updatedAt', 'type' => Database::VAR_DATETIME, 'status' => 'available', @@ -2878,51 +2890,51 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') // lengths hidden by default $lengths = []; - foreach ($attributes as $i => $attribute) { + foreach ($columns as $i => $column) { // find attribute metadata in collection document - $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); + $columnIndex = \array_search($column, array_column($oldColumns, 'key')); - if ($attributeIndex === false) { - throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute . '. Verify the attribute name or create the attribute.'); + if ($columnIndex === false) { + throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown column: ' . $column . '. Verify the column name or create the column.'); } - $attributeStatus = $oldAttributes[$attributeIndex]['status']; - $attributeType = $oldAttributes[$attributeIndex]['type']; - $attributeArray = $oldAttributes[$attributeIndex]['array'] ?? false; + $columnStatus = $oldColumns[$columnIndex]['status']; + $columnType = $oldColumns[$columnIndex]['type']; + $columnArray = $oldColumns[$columnIndex]['array'] ?? false; - if ($attributeType === Database::VAR_RELATIONSHIP) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Cannot create an index for a relationship attribute: ' . $oldAttributes[$attributeIndex]['key']); + if ($columnType === Database::VAR_RELATIONSHIP) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Cannot create an index for a relationship column: ' . $oldColumns[$columnIndex]['key']); } // ensure attribute is available - if ($attributeStatus !== 'available') { - throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Attribute not available: ' . $oldAttributes[$attributeIndex]['key']); + if ($columnStatus !== 'available') { + throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Column not available: ' . $oldColumns[$columnIndex]['key']); } $lengths[$i] = null; - if ($attributeArray === true) { + if ($columnArray === true) { $lengths[$i] = Database::ARRAY_INDEX_LENGTH; $orders[$i] = null; } } $index = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), 'key' => $key, 'status' => 'processing', // processing, available, failed, deleting, stuck 'databaseInternalId' => $db->getInternalId(), 'databaseId' => $databaseId, - 'collectionInternalId' => $collection->getInternalId(), - 'collectionId' => $collectionId, + 'collectionInternalId' => $table->getInternalId(), + 'collectionId' => $tableId, 'type' => $type, - 'attributes' => $attributes, + 'attributes' => $columns, 'lengths' => $lengths, 'orders' => $orders, ]); $validator = new IndexValidator( - $collection->getAttribute('attributes'), + $table->getAttribute('attributes'), $dbForProject->getAdapter()->getMaxIndexLength(), $dbForProject->getAdapter()->getInternalIndexesKeys(), ); @@ -2936,19 +2948,19 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::INDEX_ALREADY_EXISTS); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) ->setDatabase($db) - ->setCollection($collection) + ->setCollection($table) ->setDocument($index); $queueForEvents ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $table->getId()) ->setParam('indexId', $index->getId()) - ->setContext('collection', $collection) + ->setContext('table', $table) ->setContext('database', $db); $response @@ -2956,8 +2968,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->dynamic($index, Response::MODEL_INDEX); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') - ->alias('/v1/database/collections/:collectionId/indexes') +App::get('/v1/databases/:databaseId/tables/:tableId/indexes') + ->alias('/v1/databases/:databaseId/collections/:tableId/indexes') ->desc('List indexes') ->groups(['api', 'database']) ->label('scope', 'collections.read') @@ -2977,11 +2989,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { + ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject) { /** @var Document $database */ $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2989,9 +3001,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3000,7 +3012,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') \array_push( $queries, Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]), + Query::equal('collectionId', [$tableId]), ); /** @@ -3019,7 +3031,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') $indexId = $cursor->getValue(); $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ - Query::equal('collectionInternalId', [$collection->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$indexId]), Query::limit(1) @@ -3037,7 +3049,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') $total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT); $indexes = $dbForProject->find('indexes', $queries); } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); } $response->dynamic(new Document([ @@ -3046,8 +3058,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ]), Response::MODEL_INDEX_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') - ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) +App::get('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') ->desc('Get index') ->groups(['api', 'database']) ->label('scope', 'collections.read') @@ -3067,24 +3079,24 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { + ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $index = $collection->find('key', $key, 'indexes'); + $index = $table->find('key', $key, 'indexes'); if (empty($index)) { throw new Exception(Exception::INDEX_NOT_FOUND); } @@ -3093,15 +3105,15 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') }); -App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') - ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) +App::delete('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->alias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') ->desc('Delete index') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update') + ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') ->label('audits.event', 'index.delete') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', group: 'indexes', @@ -3117,26 +3129,26 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); if (empty($index->getId())) { throw new Exception(Exception::INDEX_NOT_FOUND); @@ -3147,34 +3159,34 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) ->setDatabase($db) - ->setCollection($collection) + ->setCollection($table) ->setDocument($index); $queueForEvents ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $table->getId()) ->setParam('indexId', $index->getId()) - ->setContext('collection', $collection) + ->setContext('table', $table) ->setContext('database', $db) ->setPayload($response->output($index, Response::MODEL_INDEX)); $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/documents') - ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) - ->desc('Create document') +App::post('/v1/databases/:databaseId/tables/:tableId/rows') + ->alias('/v1/databases/:databaseId/collections/:tableId/documents') + ->desc('Create row') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'document.create') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('audits.event', 'row.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -3183,8 +3195,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') [ new Method( namespace: 'databases', - group: 'documents', - name: 'createDocument', + group: 'rows', + name: 'createRow', description: '/docs/references/databases/create-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ @@ -3198,16 +3210,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ] ) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') - ->param('data', [], new JSON(), 'Document data as JSON object.') + ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.') + ->param('data', [], new JSON(), 'Row data as JSON object.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->inject('response') ->inject('dbForProject') ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3228,9 +3240,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3273,38 +3285,38 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } } - $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers - $data['$id'] = $documentId == 'unique()' ? ID::unique() : $documentId; + $data['$collection'] = $table->getId(); // Adding this param to make API easier for developers + $data['$id'] = $rowId == 'unique()' ? ID::unique() : $rowId; $data['$permissions'] = $permissions; - $document = new Document($data); + $row = new Document($data); $operations = 0; - $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) { + $checkPermissions = function (Document $table, Document $row, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) { $operations++; - $documentSecurity = $collection->getAttribute('documentSecurity', false); + $documentSecurity = $table->getAttribute('documentSecurity', false); $validator = new Authorization($permission); - $valid = $validator->isValid($collection->getPermissionsByType($permission)); + $valid = $validator->isValid($table->getPermissionsByType($permission)); if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } if ($permission === Database::PERMISSION_UPDATE) { - $valid = $valid || $validator->isValid($document->getUpdate()); + $valid = $valid || $validator->isValid($row->getUpdate()); if ($documentSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } } $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { continue; @@ -3318,9 +3330,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $relations = [$related]; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) ); foreach ($relations as &$relation) { @@ -3334,7 +3346,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } if ($relation instanceof Document) { $current = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) + fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId()) ); if ($current->isEmpty()) { @@ -3346,26 +3358,26 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } else { $relation->removeAttribute('$collectionId'); $relation->removeAttribute('$databaseId'); - $relation->setAttribute('$collection', $relatedCollection->getId()); + $relation->setAttribute('$collection', $relatedTable->getId()); $type = Database::PERMISSION_UPDATE; } - $checkPermissions($relatedCollection, $relation, $type); + $checkPermissions($relatedTable, $relation, $type); } } if ($isList) { - $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); } else { - $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + $row->setAttribute($relationship->getAttribute('key'), \reset($relations)); } } }; - $checkPermissions($collection, $document, Database::PERMISSION_CREATE); + $checkPermissions($table, $row, Database::PERMISSION_CREATE); try { - $document = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document); + $row = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $row); } catch (StructureException $e) { throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); } catch (DuplicateException $e) { @@ -3375,18 +3387,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } - // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { continue; @@ -3395,20 +3407,20 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $related = [$related]; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) ); foreach ($related as $relation) { if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); + $processRow($relatedTable, $relation); } } } }; - $processDocument($collection, $document); + $processRow($table, $row); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) @@ -3418,35 +3430,35 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $response ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($document, Response::MODEL_DOCUMENT); + ->dynamic($row, Response::MODEL_DOCUMENT); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); $queueForEvents ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setContext('collection', $collection) + ->setParam('tableId', $table->getId()) + ->setParam('rowId', $row->getId()) + ->setContext('table', $table) ->setContext('database', $database) ->setPayload($response->getPayload(), sensitive: $relationships); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/documents') - ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) - ->desc('List documents') +App::get('/v1/databases/:databaseId/tables/:tableId/rows') + ->alias('/v1/databases/:databaseId/collections/:tableId/documents') + ->desc('List rows') ->groups(['api', 'database']) ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'documents', - name: 'listDocuments', + group: 'rows', + name: 'listRows', description: '/docs/references/databases/list-documents.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ @@ -3458,12 +3470,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -3472,9 +3484,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3499,44 +3511,44 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $documentId = $cursor->getValue(); + $rowId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $cursorRow = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - if ($cursorDocument->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$documentId}' for the 'cursor' value not found."); + if ($cursorRow->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Row '{$rowId}' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument); + $cursor->setValue($cursorRow); } try { - $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); + $rows = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); } $operations = 0; - // Add $collectionId and $databaseId for all documents - $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations): bool { - if ($document->isEmpty()) { + // Add $tableId and $databaseId for all rows + $processRow = (function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations): bool { + if ($row->isEmpty()) { return false; } $operations++; - $document->removeAttribute('$collection'); - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); + $row->removeAttribute('$collection'); + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { if (\in_array(\gettype($related), ['array', 'object'])) { @@ -3552,30 +3564,30 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $relations = $related; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedTableId = $relationship->getAttribute('relatedCollection'); // todo: Use local cache for this getDocument - $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + $relatedTable = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)); foreach ($relations as $index => $doc) { if ($doc instanceof Document) { - if (!$processDocument($relatedCollection, $doc)) { + if (!$processRow($relatedTable, $doc)) { unset($relations[$index]); } } } if (\is_array($related)) { - $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); } elseif (empty($relations)) { - $document->setAttribute($relationship->getAttribute('key'), null); + $row->setAttribute($relationship->getAttribute('key'), null); } } return true; }); - foreach ($documents as $document) { - $processDocument($collection, $document); + foreach ($rows as $row) { + $processRow($table, $row); } $queueForStatsUsage @@ -3590,43 +3602,43 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') // Check if the SELECT query includes $databaseId and $collectionId $hasDatabaseId = false; - $hasCollectionId = false; + $hasTableId = false; if ($select) { $hasDatabaseId = \array_reduce($queries, function ($result, $query) { return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues())); }, false); - $hasCollectionId = \array_reduce($queries, function ($result, $query) { + $hasTableId = \array_reduce($queries, function ($result, $query) { return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues())); }, false); } if ($select) { - foreach ($documents as $document) { + foreach ($rows as $row) { if (!$hasDatabaseId) { - $document->removeAttribute('$databaseId'); + $row->removeAttribute('$databaseId'); } - if (!$hasCollectionId) { - $document->removeAttribute('$collectionId'); + if (!$hasTableId) { + $row->removeAttribute('$collectionId'); } } } $response->dynamic(new Document([ 'total' => $total, - 'documents' => $documents, + 'documents' => $rows, ]), Response::MODEL_DOCUMENT_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') - ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) - ->desc('Get document') +App::get('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') + ->desc('Get row') ->groups(['api', 'database']) ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'documents', - name: 'getDocument', + group: 'rows', + name: 'getRow', description: '/docs/references/databases/get-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ @@ -3638,13 +3650,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('documentId', '', new UID(), 'Document ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('rowId', '', new UID(), 'Row ID.') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $tableId, string $rowId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -3653,45 +3665,45 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } try { $queries = Query::parseQueries($queries); - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); + $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId, $queries); } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - if ($document->isEmpty()) { + if ($row->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $operations = 0; - // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations) { - if ($document->isEmpty()) { + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations) { + if ($row->isEmpty()) { return; } $operations++; - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { if (\in_array(\gettype($related), ['array', 'object'])) { @@ -3705,20 +3717,20 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $related = [$related]; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) ); foreach ($related as $relation) { if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); + $processRow($relatedTable, $relation); } } } }; - $processDocument($collection, $document); + $processRow($table, $row); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) @@ -3726,19 +3738,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $response->addHeader('X-Debug-Operations', $operations); - $response->dynamic($document, Response::MODEL_DOCUMENT); + $response->dynamic($row, Response::MODEL_DOCUMENT); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') - ->alias('/v1/database/collections/:collectionId/documents/:documentId/logs', ['databaseId' => 'default']) - ->desc('List document logs') +App::get('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs') + ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId/logs') + ->desc('List row logs') ->groups(['api', 'database']) ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', group: 'logs', - name: 'listDocumentLogs', + name: 'listRowLogs', description: '/docs/references/databases/get-document-logs.md', auth: [AuthType::ADMIN], responses: [ @@ -3750,14 +3762,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen contentType: ContentType::JSON, )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID.') - ->param('documentId', '', new UID(), 'Document ID.') + ->param('tableId', '', new UID(), 'Collection ID.') + ->param('rowId', '', new UID(), 'Row ID.') ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) ->inject('response') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->action(function (string $databaseId, string $tableId, string $rowId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -3765,15 +3777,15 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId); + $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId); - if ($document->isEmpty()) { + if ($row->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } @@ -3790,7 +3802,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ]); $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId(); + $resource = 'database/' . $databaseId . '/table/' . $tableId . '/row/' . $row->getId(); $logs = $audit->getLogsByResource($resource, $queries); $output = []; @@ -3844,22 +3856,22 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ]), Response::MODEL_LOG_LIST); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') - ->alias('/v1/database/collections/:collectionId/documents/:documentId') - ->desc('Update document') +App::patch('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') + ->desc('Update row') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update') + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].update') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'document.update') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') + ->label('audits.event', 'row.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( namespace: 'databases', - group: 'documents', - name: 'updateDocument', + group: 'rows', + name: 'updateRow', description: '/docs/references/databases/update-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ @@ -3871,16 +3883,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID.') - ->param('documentId', '', new UID(), 'Document ID.') - ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) + ->param('tableId', '', new UID(), 'Collection ID.') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3897,17 +3909,17 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for update - /** @var Document $document */ - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + /** @var Document $row */ + $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - if ($document->isEmpty()) { + if ($row->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } @@ -3940,16 +3952,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum } if (\is_null($permissions)) { - $permissions = $document->getPermissions() ?? []; + $permissions = $row->getPermissions() ?? []; } - $data['$id'] = $documentId; + $data['$id'] = $rowId; $data['$permissions'] = $permissions; - $newDocument = new Document($data); + $newRow = new Document($data); $operations = 0; - $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, &$operations) { + $setTable = (function (Document $collection, Document $document) use (&$setTable, $dbForProject, $database, &$operations) { $operations++; @@ -3973,9 +3985,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $relations = [$related]; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) ); foreach ($relations as &$relation) { @@ -3989,8 +4001,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $relation = new Document($relation); } if ($relation instanceof Document) { - $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( - 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + $oldRow = Authorization::skip(fn () => $dbForProject->getDocument( + 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId() )); $relation->removeAttribute('$collectionId'); @@ -3998,15 +4010,15 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum // Attribute $collection is required for Utopia. $relation->setAttribute( '$collection', - 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId() + 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId() ); - if ($oldDocument->isEmpty()) { + if ($oldRow->isEmpty()) { if (isset($relation['$id']) && $relation['$id'] === 'unique()') { $relation['$id'] = ID::unique(); } } - $setCollection($relatedCollection, $relation); + $setTable($relatedTable, $relation); } } @@ -4018,7 +4030,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum } }); - $setCollection($collection, $newDocument); + $setTable($table, $newRow); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) @@ -4027,12 +4039,12 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $response->addHeader('X-Debug-Operations', $operations); try { - $document = $dbForProject->withRequestTimestamp( + $row = $dbForProject->withRequestTimestamp( $requestTimestamp, fn () => $dbForProject->updateDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - $document->getId(), - $newDocument + 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), + $row->getId(), + $newRow ) ); } catch (AuthorizationException) { @@ -4045,18 +4057,18 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::COLLECTION_NOT_FOUND); } - // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { continue; @@ -4065,56 +4077,56 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $related = [$related]; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) ); foreach ($related as $relation) { if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); + $processRow($relatedTable, $relation); } } } }; - $processDocument($collection, $document); + $processRow($table, $row); - $response->dynamic($document, Response::MODEL_DOCUMENT); + $response->dynamic($row, Response::MODEL_DOCUMENT); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); $queueForEvents ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setContext('collection', $collection) + ->setParam('tableId', $table->getId()) + ->setParam('rowId', $row->getId()) + ->setContext('table', $table) ->setContext('database', $database) ->setPayload($response->getPayload(), sensitive: $relationships); }); -App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') - ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) - ->desc('Delete document') +App::delete('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') + ->desc('Delete row') ->groups(['api', 'database']) ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete') - ->label('audits.event', 'document.delete') - ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}') + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].delete') + ->label('audits.event', 'row.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{request.rowId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( namespace: 'databases', - group: 'documents', - name: 'deleteDocument', + group: 'rows', + name: 'deleteRow', description: '/docs/references/databases/delete-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ @@ -4126,14 +4138,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('documentId', '', new UID(), 'Document ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('rowId', '', new UID(), 'Row ID.') ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -4143,42 +4155,42 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for delete - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - if ($document->isEmpty()) { + if ($row->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } try { - $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) { + $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $table, $rowId) { $dbForProject->deleteDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - $documentId + 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), + $rowId ); }); } catch (NotFoundException $e) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { continue; @@ -4187,20 +4199,20 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $related = [$related]; } - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) ); foreach ($related as $relation) { if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); + $processRow($relatedTable, $relation); } } } }; - $processDocument($collection, $document); + $processRow($table, $row); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) @@ -4211,18 +4223,18 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $relationships = \array_map( fn ($document) => $document->getAttribute('key'), \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); $queueForEvents ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setContext('collection', $collection) + ->setParam('tableId', $table->getId()) + ->setParam('rowId', $row->getId()) + ->setContext('table', $table) ->setContext('database', $database) - ->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships); + ->setPayload($response->output($row, Response::MODEL_DOCUMENT), sensitive: $relationships); $response->noContent(); }); @@ -4424,16 +4436,16 @@ App::get('/v1/databases/:databaseId/usage') ]), Response::MODEL_USAGE_DATABASE); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/usage') - ->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default']) - ->desc('Get collection usage stats') +App::get('/v1/databases/:databaseId/tables/:tableId/usage') + ->alias('/v1/databases/:databaseId/collections/:tableId/usage') + ->desc('Get table usage stats') ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', group: null, - name: 'getCollectionUsage', + name: 'getTableUsage', description: '/docs/references/databases/get-collection-usage.md', auth: [AuthType::ADMIN], responses: [ @@ -4446,16 +4458,16 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) - ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('tableId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject) { + ->action(function (string $databaseId, string $range, string $tableId, Response $response, Database $dbForProject) { $database = $dbForProject->getDocument('databases', $databaseId); - $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -4463,7 +4475,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), + str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $tableDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { From 4f1f9bb4a23281a8088385b8065cfba64c8acc09 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 26 Apr 2025 16:06:53 +0530 Subject: [PATCH 002/173] add: filter for latest changes. --- app/controllers/general.php | 3 +- src/Appwrite/Utopia/Request/Filters/V19.php | 32 +++++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 889571fe62..bf048be9d6 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -805,7 +805,8 @@ App::init() if (version_compare($requestFormat, '1.6.0', '<')) { $request->addFilter(new RequestV18()); } - if (version_compare($requestFormat, '1.7.0', '<')) { + // alias filters on 1.7.x, so we use `<=` and not just `<` + if (version_compare($requestFormat, '1.7.0', '<=')) { $request->addFilter(new RequestV19()); } } diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index 041c126a69..9597f40570 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -6,18 +6,32 @@ use Appwrite\Utopia\Request\Filter; class V19 extends Filter { - // Convert 1.6 params to 1.7 public function parse(array $content, string $model): array { - /* - Uncomment with first request filter; current is just a copy of V18 - switch ($model) { - case 'functions.create': - $content['something'] = $content['somethingElse'] ?? ""; - unset($content['something']); - break; + return $this->overrideDatabaseParams($content, $model); + } + + // Database terminology change handling. + protected function overrideDatabaseParams(array $content, string $model): array + { + if (!str_starts_with($model, 'databases.')) { + return $content; + } + + $map = [ + 'collectionId' => 'tableId', + 'attributeId' => 'columnId', + 'attributes' => 'columns', + 'documentId' => 'rowId', + 'relatedCollectionId' => 'relatedTableId' + ]; + + foreach ($map as $oldKey => $newKey) { + if (isset($content[$oldKey])) { + $content[$newKey] = $content[$oldKey]; + unset($content[$oldKey]); + } } - */ return $content; } From 21715448ac04b154df005e79135cb2f6ee6fdd33 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 26 Apr 2025 16:07:32 +0530 Subject: [PATCH 003/173] fix|update: database tests for new error messages. --- tests/e2e/Services/Databases/DatabasesBase.php | 4 ++-- tests/e2e/Services/Databases/DatabasesCustomClientTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 0c7e1d386e..989d1217d3 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1373,7 +1373,7 @@ trait DatabasesBase ]); $this->assertEquals(400, $unknown['headers']['status-code']); - $this->assertEquals('Unknown attribute: Unknown. Verify the attribute name or create the attribute.', $unknown['body']['message']); + $this->assertEquals('Unknown column: Unknown. Verify the column name or create the column.', $unknown['body']['message']); $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -2671,7 +2671,7 @@ trait DatabasesBase $this->assertEquals(400, $enumDefault['headers']['status-code']); $this->assertEquals(400, $enumDefaultStrict['headers']['status-code']); $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']); - $this->assertEquals('Cannot set default value for array attributes', $defaultArray['body']['message']); + $this->assertEquals('Cannot set default value for array columns', $defaultArray['body']['message']); $this->assertEquals(400, $datetimeDefault['headers']['status-code']); // wait for worker to add attributes sleep(3); diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index 8484996058..d4a8c87868 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -472,7 +472,7 @@ class DatabasesCustomClientTest extends Scope \sleep(2); $this->assertEquals(409, $relation['body']['code']); - $this->assertEquals('Creating more than one "manyToMany" relationship on the same collection is currently not permitted.', $relation['body']['message']); + $this->assertEquals('Creating more than one "manyToMany" relationship on the same table is currently not permitted.', $relation['body']['message']); } public function testUpdateWithoutRelationPermission(): void From 89ce5a6e1044b6866613760381172a4b6fb727c3 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 26 Apr 2025 16:07:46 +0530 Subject: [PATCH 004/173] add: todo. --- app/config/scopes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/config/scopes.php b/app/config/scopes.php index 7dea7b1cd5..d459ffb2fc 100644 --- a/app/config/scopes.php +++ b/app/config/scopes.php @@ -1,5 +1,6 @@ [ 'description' => 'Access to create, update, and delete user sessions', From 949f58522d7c46d6f6cb0bb782ed6fc8eecd576b Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 27 Apr 2025 10:33:44 +0530 Subject: [PATCH 005/173] change: events system. --- app/config/events.php | 28 +- app/config/templates/function.php | 2 +- app/controllers/api/databases.php | 21 +- app/controllers/general.php | 7 +- src/Appwrite/Event/Database.php | 36 +-- src/Appwrite/Event/Realtime.php | 4 +- src/Appwrite/Messaging/Adapter/Realtime.php | 35 +-- src/Appwrite/Platform/Workers/Databases.php | 284 +++++++++--------- .../Request/Filters/DatabaseAliases.php | 38 +++ src/Appwrite/Utopia/Request/Filters/V19.php | 32 +- .../Utopia/Response/Model/Webhook.php | 2 +- 11 files changed, 254 insertions(+), 235 deletions(-) create mode 100644 src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php diff --git a/app/config/events.php b/app/config/events.php index 0bfddf4f1f..3b0cc982a0 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -95,22 +95,22 @@ return [ '$model' => Response::MODEL_DATABASE, '$resource' => true, '$description' => 'This event triggers on any database event.', - 'collections' => [ + 'tables' => [ '$model' => Response::MODEL_COLLECTION, '$resource' => true, - '$description' => 'This event triggers on any collection event.', - 'documents' => [ + '$description' => 'This event triggers on any table event.', + 'rows' => [ '$model' => Response::MODEL_DOCUMENT, '$resource' => true, - '$description' => 'This event triggers on any documents event.', + '$description' => 'This event triggers on any rows event.', 'create' => [ - '$description' => 'This event triggers when a document is created.', + '$description' => 'This event triggers when a row is created.', ], 'delete' => [ - '$description' => 'This event triggers when a document is deleted.' + '$description' => 'This event triggers when a row is deleted.' ], 'update' => [ - '$description' => 'This event triggers when a document is updated.' + '$description' => 'This event triggers when a row is updated.' ], ], 'indexes' => [ @@ -124,25 +124,25 @@ return [ '$description' => 'This event triggers when an index is deleted.' ] ], - 'attributes' => [ + 'columns' => [ '$model' => Response::MODEL_ATTRIBUTE, '$resource' => true, - '$description' => 'This event triggers on any attributes event.', + '$description' => 'This event triggers on any columns event.', 'create' => [ - '$description' => 'This event triggers when an attribute is created.', + '$description' => 'This event triggers when an column is created.', ], 'delete' => [ - '$description' => 'This event triggers when an attribute is deleted.' + '$description' => 'This event triggers when an column is deleted.' ] ], 'create' => [ - '$description' => 'This event triggers when a collection is created.' + '$description' => 'This event triggers when a table is created.' ], 'delete' => [ - '$description' => 'This event triggers when a collection is deleted.', + '$description' => 'This event triggers when a table is deleted.', ], 'update' => [ - '$description' => 'This event triggers when a collection is updated.', + '$description' => 'This event triggers when a table is updated.', ] ], 'create' => [ diff --git a/app/config/templates/function.php b/app/config/templates/function.php index d8426ad900..b017281dbe 100644 --- a/app/config/templates/function.php +++ b/app/config/templates/function.php @@ -1395,7 +1395,7 @@ return [ 'score' => 5, 'tagline' => 'Convert text to speech using the Hugging Face inference API.', 'permissions' => ['any'], - 'events' => ['databases.*.collections.*.documents.*.create'], + 'events' => ['databases.*.tables.*.rows.*.create'], 'cron' => '', 'timeout' => 15, 'useCases' => ['ai'], diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index d3a34f56e8..d8556dd4fe 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -218,8 +218,8 @@ function createColumn(string $databaseId, string $tableId, Document $column, Res $queueForDatabase ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) ->setDatabase($db) - ->setCollection($table) - ->setDocument($column); + ->setTable($table) + ->setRow($column); $queueForEvents ->setContext('table', $table) @@ -485,8 +485,7 @@ App::post('/v1/databases') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) { $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; @@ -1280,7 +1279,7 @@ App::delete('/v1/databases/:databaseId/tables/:tableId') $queueForDatabase ->setType(DATABASE_TYPE_DELETE_COLLECTION) ->setDatabase($database) - ->setCollection($table); + ->setTable($table); $queueForEvents ->setContext('database', $database) @@ -2759,9 +2758,9 @@ App::delete('/v1/databases/:databaseId/tables/:tableId/columns/:key') $queueForDatabase ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setCollection($table) + ->setTable($table) ->setDatabase($db) - ->setDocument($column); + ->setRow($column); // Select response model based on type and format $type = $column->getAttribute('type'); @@ -2953,8 +2952,8 @@ App::post('/v1/databases/:databaseId/tables/:tableId/indexes') $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) ->setDatabase($db) - ->setCollection($table) - ->setDocument($index); + ->setTable($table) + ->setRow($index); $queueForEvents ->setParam('databaseId', $databaseId) @@ -3164,8 +3163,8 @@ App::delete('/v1/databases/:databaseId/tables/:tableId/indexes/:key') $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) ->setDatabase($db) - ->setCollection($table) - ->setDocument($index); + ->setTable($table) + ->setRow($index); $queueForEvents ->setParam('databaseId', $databaseId) diff --git a/app/controllers/general.php b/app/controllers/general.php index bf048be9d6..fdef1e9cee 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -18,6 +18,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Transformation\Adapter\Preview; use Appwrite\Transformation\Transformation; use Appwrite\Utopia\Request; +use Appwrite\Utopia\Request\Filters\DatabaseAliases; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; use Appwrite\Utopia\Request\Filters\V18 as RequestV18; @@ -805,12 +806,14 @@ App::init() if (version_compare($requestFormat, '1.6.0', '<')) { $request->addFilter(new RequestV18()); } - // alias filters on 1.7.x, so we use `<=` and not just `<` - if (version_compare($requestFormat, '1.7.0', '<=')) { + if (version_compare($requestFormat, '1.7.0', '<')) { $request->addFilter(new RequestV19()); } } + // process on all databases endpoints! + $request->addFilter(new DatabaseAliases()); + $domain = $request->getHostname(); $domains = Config::getParam('domains', []); if (!array_key_exists($domain, $domains)) { diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index d2f70dddf2..797574d2fc 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -10,8 +10,8 @@ class Database extends Event { protected string $type = ''; protected ?Document $database = null; - protected ?Document $collection = null; - protected ?Document $document = null; + protected ?Document $table = null; + protected ?Document $row = null; public function __construct(protected Publisher $publisher) { @@ -55,48 +55,48 @@ class Database extends Event } /** - * Set the collection for this database event. + * Set the table for this database event. * - * @param Document $collection + * @param Document $table * @return self */ - public function setCollection(Document $collection): self + public function setTable(Document $table): self { - $this->collection = $collection; + $this->table = $table; return $this; } /** - * Returns set collection for this event. + * Returns set table for this event. * * @return null|Document */ - public function getCollection(): ?Document + public function getTable(): ?Document { - return $this->collection; + return $this->table; } /** - * Set the document for this database event. + * Set the row for this database event. * - * @param Document $document + * @param Document $row * @return self */ - public function setDocument(Document $document): self + public function setRow(Document $row): self { - $this->document = $document; + $this->row = $row; return $this; } /** - * Returns set document for this database event. + * Returns set row for this database event. * @return null|Document */ - public function getDocument(): ?Document + public function getRow(): ?Document { - return $this->document; + return $this->row; } public function getQueue(): string @@ -123,8 +123,8 @@ class Database extends Event 'project' => $this->project, 'user' => $this->user, 'type' => $this->type, - 'collection' => $this->collection, - 'document' => $this->document, + 'table' => $this->table, + 'row' => $this->row, 'database' => $this->database, 'events' => Event::generateEvents($this->getEvent(), $this->getParams()) ]; diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index 4d8c9a321b..e1ec891e67 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -76,7 +76,7 @@ class Realtime extends Event $payload = new Document($this->getPayload()); $db = $this->getContext('database'); - $collection = $this->getContext('collection'); + $table = $this->getContext('table'); $bucket = $this->getContext('bucket'); $target = RealtimeAdapter::fromPayload( @@ -85,7 +85,7 @@ class Realtime extends Event payload: $payload, project: $this->getProject(), database: $db, - collection: $collection, + table: $table, bucket: $bucket, ); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 568132ceb1..400589f677 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -252,12 +252,12 @@ class Realtime extends Adapter * @param Document $payload * @param Document|null $project * @param Document|null $database - * @param Document|null $collection + * @param Document|null $table * @param Document|null $bucket * @return array * @throws \Exception */ - public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $collection = null, Document $bucket = null): array + public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $table = null, Document $bucket = null): array { $channels = []; $roles = []; @@ -273,6 +273,7 @@ class Realtime extends Adapter $roles = [Role::user(ID::custom($parts[1]))->toString()]; break; case 'rules': + case 'migrations': $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); $projectId = 'console'; @@ -297,26 +298,26 @@ class Realtime extends Adapter $roles = [Role::team(ID::custom($parts[1]))->toString()]; break; case 'databases': - if (in_array($parts[4] ?? [], ['attributes', 'indexes'])) { + if (in_array($parts[4] ?? [], ['columns', 'indexes'])) { $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); $projectId = 'console'; $roles = [Role::team($project->getAttribute('teamId'))->toString()]; - } elseif (($parts[4] ?? '') === 'documents') { + } elseif (($parts[4] ?? '') === 'rows') { if ($database->isEmpty()) { - throw new \Exception('Database needs to be passed to Realtime for Document events in the Database.'); + throw new \Exception('Database needs to be passed to Realtime for Row events in the Database.'); } - if ($collection->isEmpty()) { - throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.'); + if ($table->isEmpty()) { + throw new \Exception('Table needs to be passed to Realtime for Row events in the Database.'); } - $channels[] = 'documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId(); + $channels[] = 'rows'; + $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$collectionId') . '.rows'; + $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$collectionId') . '.rows.' . $payload->getId(); - $roles = $collection->getAttribute('documentSecurity', false) - ? \array_merge($collection->getRead(), $payload->getRead()) - : $collection->getRead(); + $roles = $table->getAttribute('documentSecurity', false) + ? \array_merge($table->getRead(), $payload->getRead()) + : $table->getRead(); } break; case 'buckets': @@ -334,7 +335,6 @@ class Realtime extends Adapter } break; - case 'functions': if ($parts[2] === 'executions') { if (!empty($payload->getRead())) { @@ -353,7 +353,6 @@ class Realtime extends Adapter } break; - case 'sites': if ($parts[2] === 'deployments') { $channels[] = 'console'; @@ -362,12 +361,6 @@ class Realtime extends Adapter $roles = [Role::team($project->getAttribute('teamId'))->toString()]; } - break; - case 'migrations': - $channels[] = 'console'; - $channels[] = 'projects.' . $project->getId(); - $projectId = 'console'; - $roles = [Role::team($project->getAttribute('teamId'))->toString()]; break; } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index b2691b420e..1f3b76d6eb 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -60,8 +60,8 @@ class Databases extends Action } $type = $payload['type']; - $collection = new Document($payload['collection'] ?? []); - $document = new Document($payload['document'] ?? []); + $row = new Document($payload['row'] ?? []); + $table = new Document($payload['table'] ?? []); $database = new Document($payload['database'] ?? []); $log->addTag('projectId', $project->getId()); @@ -75,19 +75,19 @@ class Databases extends Action match (\strval($type)) { DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $project, $dbForProject), - DATABASE_TYPE_DELETE_COLLECTION => $this->deleteCollection($database, $collection, $project, $dbForProject), - DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_COLLECTION => $this->deleteTable($database, $table, $project, $dbForProject), + DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createColumn($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteColumn($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), default => throw new Exception('No database operation for type: ' . \strval($type)), }; } /** * @param Document $database - * @param Document $collection - * @param Document $attribute + * @param Document $table + * @param Document $column * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject @@ -98,64 +98,64 @@ class Databases extends Action * @throws \Exception * @throws \Throwable */ - private function createAttribute( + private function createColumn( Document $database, - Document $collection, - Document $attribute, + Document $table, + Document $column, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime ): void { - if ($collection->isEmpty()) { - throw new Exception('Missing collection'); + if ($table->isEmpty()) { + throw new Exception('Missing table'); } - if ($attribute->isEmpty()) { - throw new Exception('Missing attribute'); + if ($column->isEmpty()) { + throw new Exception('Missing column'); } $projectId = $project->getId(); - $event = "databases.[databaseId].collections.[collectionId].attributes.[attributeId].update"; + $event = "databases.[databaseId].tables.[tableId].columns.[columnId].update"; /** * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing informations. */ - $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); + $column = $dbForProject->getDocument('attributes', $column->getId()); - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { // Attribute was deleted before job was processed return; } - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); - $size = $attribute->getAttribute('size', 0); - $required = $attribute->getAttribute('required', false); - $default = $attribute->getAttribute('default', null); - $signed = $attribute->getAttribute('signed', true); - $array = $attribute->getAttribute('array', false); - $format = $attribute->getAttribute('format', ''); - $formatOptions = $attribute->getAttribute('formatOptions', []); - $filters = $attribute->getAttribute('filters', []); - $options = $attribute->getAttribute('options', []); + $tableId = $table->getId(); + $key = $column->getAttribute('key', ''); + $type = $column->getAttribute('type', ''); + $size = $column->getAttribute('size', 0); + $required = $column->getAttribute('required', false); + $default = $column->getAttribute('default', null); + $signed = $column->getAttribute('signed', true); + $array = $column->getAttribute('array', false); + $format = $column->getAttribute('format', ''); + $formatOptions = $column->getAttribute('formatOptions', []); + $filters = $column->getAttribute('filters', []); + $options = $column->getAttribute('options', []); $project = $dbForPlatform->getDocument('projects', $projectId); - $relatedAttribute = new Document(); - $relatedCollection = new Document(); + $relatedColumn = new Document(); + $relatedTable = new Document(); try { switch ($type) { case Database::VAR_RELATIONSHIP: - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { - throw new DatabaseException('Collection not found'); + $relatedTable = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedTable->isEmpty()) { + throw new DatabaseException('Table not found'); } if ( !$dbForProject->createRelationship( - collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + collection: 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), + relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), type: $options['relationType'], twoWay: $options['twoWay'], id: $key, @@ -163,61 +163,61 @@ class Databases extends Action onDelete: $options['onDelete'], ) ) { - throw new DatabaseException('Failed to create Attribute'); + throw new DatabaseException('Failed to create Column'); } if ($options['twoWay']) { - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available')); + $relatedColumn = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); + $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'available')); } break; default: - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create Column'); } } - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); + $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'available')); } catch (\Throwable $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { - $attribute->setAttribute('error', $e->getMessage()); - if (! $relatedAttribute->isEmpty()) { - $relatedAttribute->setAttribute('error', $e->getMessage()); + $column->setAttribute('error', $e->getMessage()); + if (! $relatedColumn->isEmpty()) { + $relatedColumn->setAttribute('error', $e->getMessage()); } } $dbForProject->updateDocument( 'attributes', - $attribute->getId(), - $attribute->setAttribute('status', 'failed') + $column->getId(), + $column->setAttribute('status', 'failed') ); - if (! $relatedAttribute->isEmpty()) { + if (! $relatedColumn->isEmpty()) { $dbForProject->updateDocument( 'attributes', - $relatedAttribute->getId(), - $relatedAttribute->setAttribute('status', 'failed') + $relatedColumn->getId(), + $relatedColumn->setAttribute('status', 'failed') ); } throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); + $this->trigger($database, $table, $project, $event, $queueForRealtime, $column); - if (! $relatedCollection->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + if (! $relatedTable->isEmpty()) { + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $tableId); } } /** * @param Document $database - * @param Document $collection - * @param Document $attribute + * @param Document $table + * @param Document $column * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject @@ -228,24 +228,24 @@ class Databases extends Action * @throws \Exception * @throws \Throwable **/ - private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function deleteColumn(Document $database, Document $table, Document $column, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception('Missing collection'); } - if ($attribute->isEmpty()) { + if ($column->isEmpty()) { throw new Exception('Missing attribute'); } $projectId = $project->getId(); - $event = 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete'; - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); + $event = 'databases.[databaseId].tables.[tableId].columns.[columnId].delete'; + $tableId = $table->getId(); + $key = $column->getAttribute('key', ''); + $type = $column->getAttribute('type', ''); $project = $dbForPlatform->getDocument('projects', $projectId); - $options = $attribute->getAttribute('options', []); - $relatedAttribute = new Document(); - $relatedCollection = new Document(); + $options = $column->getAttribute('options', []); + $relatedColumn = new Document(); + $relatedTable = new Document(); // possible states at this point: // - available: should not land in queue; controller flips these to 'deleting' // - processing: hasn't finished creating @@ -257,89 +257,89 @@ class Databases extends Action try { if ($type === Database::VAR_RELATIONSHIP) { if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { - throw new DatabaseException('Collection not found'); + $relatedTable = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedTable->isEmpty()) { + throw new DatabaseException('Table not found'); } - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedColumn = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); } - if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); + if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { + $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'stuck')); throw new DatabaseException('Failed to delete Relationship'); } - } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new DatabaseException('Failed to delete Attribute'); + } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { + throw new DatabaseException('Failed to delete Column'); } - $dbForProject->deleteDocument('attributes', $attribute->getId()); + $dbForProject->deleteDocument('attributes', $column->getId()); - if (!$relatedAttribute->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); + if (!$relatedColumn->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedColumn->getId()); } } catch (NotFound $e) { Console::error($e->getMessage()); - $dbForProject->deleteDocument('attributes', $attribute->getId()); + $dbForProject->deleteDocument('attributes', $column->getId()); - if (!$relatedAttribute->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); + if (!$relatedColumn->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedColumn->getId()); } } catch (\Throwable $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { - $attribute->setAttribute('error', $e->getMessage()); - if (!$relatedAttribute->isEmpty()) { - $relatedAttribute->setAttribute('error', $e->getMessage()); + $column->setAttribute('error', $e->getMessage()); + if (!$relatedColumn->isEmpty()) { + $relatedColumn->setAttribute('error', $e->getMessage()); } } $dbForProject->updateDocument( 'attributes', - $attribute->getId(), - $attribute->setAttribute('status', 'stuck') + $column->getId(), + $column->setAttribute('status', 'stuck') ); - if (!$relatedAttribute->isEmpty()) { + if (!$relatedColumn->isEmpty()) { $dbForProject->updateDocument( 'attributes', - $relatedAttribute->getId(), - $relatedAttribute->setAttribute('status', 'stuck') + $relatedColumn->getId(), + $relatedColumn->setAttribute('status', 'stuck') ); } throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); + $this->trigger($database, $table, $project, $event, $queueForRealtime, $column); } // The underlying database removes/rebuilds indexes when attribute is removed // Update indexes table with changes /** @var Document[] $indexes */ - $indexes = $collection->getAttribute('indexes', []); + $indexes = $table->getAttribute('indexes', []); foreach ($indexes as $index) { - /** @var string[] $attributes */ - $attributes = $index->getAttribute('attributes'); + /** @var string[] $columns */ + $columns = $index->getAttribute('attributes'); $lengths = $index->getAttribute('lengths'); $orders = $index->getAttribute('orders'); - $found = \array_search($key, $attributes); + $found = \array_search($key, $columns); if ($found !== false) { // If found, remove entry from attributes, lengths, and orders // array_values wraps array_diff to reindex array keys // when found attribute is removed from array - $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); + $columns = \array_values(\array_diff($columns, [$columns[$found]])); $lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : [])); $orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : [])); - if (empty($attributes)) { + if (empty($columns)) { $dbForProject->deleteDocument('indexes', $index->getId()); } else { $index - ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) + ->setAttribute('attributes', $columns, Document::SET_TYPE_ASSIGN) ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); @@ -357,7 +357,7 @@ class Databases extends Action } if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); + $this->deleteIndex($database, $table, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); } else { $dbForProject->updateDocument('indexes', $index->getId(), $index); } @@ -365,17 +365,17 @@ class Databases extends Action } } } finally { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $tableId); - if (! $relatedCollection->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + if (! $relatedTable->isEmpty()) { + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); } } } /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $index * @param Document $project * @param Database $dbForPlatform @@ -388,9 +388,9 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function createIndex(Document $database, Document $table, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception('Missing collection'); } if ($index->isEmpty()) { @@ -398,8 +398,8 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update'; - $collectionId = $collection->getId(); + $event = 'databases.[databaseId].tables.[tableId].indexes.[indexId].update'; + $collectionId = $table->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); $attributes = $index->getAttribute('attributes', []); @@ -408,7 +408,7 @@ class Databases extends Action $project = $dbForPlatform->getDocument('projects', $projectId); try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { throw new DatabaseException('Failed to create Index'); } $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); @@ -425,14 +425,14 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); + $this->trigger($database, $table, $project, $event, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $index * @param Document $project * @param Database $dbForPlatform @@ -445,9 +445,9 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function deleteIndex(Document $database, Document $table, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($collection->isEmpty()) { + if ($table->isEmpty()) { throw new Exception('Missing collection'); } if ($index->isEmpty()) { @@ -455,13 +455,13 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete'; + $event = 'databases.[databaseId].tables.[tableId].indexes.[indexId].delete'; $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); $project = $dbForPlatform->getDocument('projects', $projectId); try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); @@ -481,8 +481,8 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); + $this->trigger($database, $table, $project, $event, $queueForRealtime, null, $index); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $table->getId()); } } @@ -496,7 +496,7 @@ class Databases extends Action protected function deleteDatabase(Document $database, Document $project, $dbForProject): void { $this->deleteByGroup('database_' . $database->getInternalId(), [], $dbForProject, function ($collection) use ($database, $project, $dbForProject) { - $this->deleteCollection($database, $collection, $project, $dbForProject); + $this->deleteTable($database, $collection, $project, $dbForProject); }); $dbForProject->deleteCollection('database_' . $database->getInternalId()); @@ -504,7 +504,7 @@ class Databases extends Action /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $project * @param Database $dbForProject * @return void @@ -515,17 +515,17 @@ class Databases extends Action * @throws Structure * @throws Exception */ - protected function deleteCollection(Document $database, Document $collection, Document $project, Database $dbForProject): void + protected function deleteTable(Document $database, Document $table, Document $project, Database $dbForProject): void { - if ($collection->isEmpty()) { - throw new Exception('Missing collection'); + if ($table->isEmpty()) { + throw new Exception('Missing table'); } - $collectionId = $collection->getId(); - $collectionInternalId = $collection->getInternalId(); + $collectionId = $table->getId(); + $collectionInternalId = $table->getInternalId(); $databaseInternalId = $database->getInternalId(); - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $table->getInternalId()); /** * Related collections relating to current collection @@ -558,50 +558,50 @@ class Databases extends Action /** - * @param string $collectionId + * @param string $tableId * @param array $queries * @param Database $database * @param callable|null $callback * @return void * @throws Exception */ - protected function deleteByGroup(string $collectionId, array $queries, Database $database, callable $callback = null): void + protected function deleteByGroup(string $tableId, array $queries, Database $database, callable $callback = null): void { $start = \microtime(true); try { $count = $database->deleteDocuments( - $collectionId, + $tableId, $queries, Database::DELETE_BATCH_SIZE, $callback ); } catch (\Throwable $th) { $tenant = $database->getSharedTables() ? 'Tenant:'.$database->getTenant() : ''; - Console::error("Failed to delete documents for collection:{$database->getNamespace()}_{$collectionId} {$tenant} :{$th->getMessage()}"); + Console::error("Failed to delete rows for table:{$database->getNamespace()}_{$tableId} {$tenant} :{$th->getMessage()}"); return; } $end = \microtime(true); - Console::info("Deleted {$count} documents by group in " . ($end - $start) . " seconds"); + Console::info("Deleted {$count} rows by group in " . ($end - $start) . " seconds"); } /** * @param Document $database - * @param Document $collection + * @param Document $table * @param Document $project * @param Realtime $queueForRealtime - * @param Document|null $attribute + * @param Document|null $column * @param Document|null $index * @return void */ protected function trigger( - Document $database, - Document $collection, - Document $project, - string $event, - Realtime $queueForRealtime, - Document|null $attribute = null, + Document $database, + Document $table, + Document $project, + string $event, + Realtime $queueForRealtime, + Document|null $column = null, Document|null $index = null, ): void { $queueForRealtime @@ -609,14 +609,14 @@ class Databases extends Action ->setSubscribers(['console']) ->setEvent($event) ->setParam('databaseId', $database->getId()) - ->setParam('collectionId', $collection->getId()); + ->setParam('tableId', $table->getId()); - if ($attribute !== null && !empty($attribute)) { + if (! empty($column)) { $queueForRealtime - ->setParam('attributeId', $attribute->getId()) - ->setPayload($attribute->getArrayCopy()); + ->setParam('columnId', $column->getId()) + ->setPayload($column->getArrayCopy()); } - if ($index !== null && !empty($index)) { + if (! empty($index)) { $queueForRealtime ->setParam('indexId', $index->getId()) ->setPayload($index->getArrayCopy()); diff --git a/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php new file mode 100644 index 0000000000..b2b772be11 --- /dev/null +++ b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php @@ -0,0 +1,38 @@ + 'rowId', + 'attributes' => 'columns', + 'collectionId' => 'tableId', + 'attributeId' => 'columnId', + 'relatedCollectionId' => 'relatedTableId' + ]; + + public function parse(array $content, string $model): array + { + return $this->overrideDatabaseParams($content, $model); + } + + protected function overrideDatabaseParams(array $content, string $model): array + { + if (!str_starts_with($model, 'databases.')) { + return $content; + } + + $intersect = array_intersect_key(self::PARAMS_MAP, $content); + + foreach ($intersect as $oldKey => $newKey) { + $content[$newKey] = $content[$oldKey]; + unset($content[$oldKey]); + } + + return $content; + } +} diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index 9597f40570..041c126a69 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -6,32 +6,18 @@ use Appwrite\Utopia\Request\Filter; class V19 extends Filter { + // Convert 1.6 params to 1.7 public function parse(array $content, string $model): array { - return $this->overrideDatabaseParams($content, $model); - } - - // Database terminology change handling. - protected function overrideDatabaseParams(array $content, string $model): array - { - if (!str_starts_with($model, 'databases.')) { - return $content; - } - - $map = [ - 'collectionId' => 'tableId', - 'attributeId' => 'columnId', - 'attributes' => 'columns', - 'documentId' => 'rowId', - 'relatedCollectionId' => 'relatedTableId' - ]; - - foreach ($map as $oldKey => $newKey) { - if (isset($content[$oldKey])) { - $content[$newKey] = $content[$oldKey]; - unset($content[$oldKey]); - } + /* + Uncomment with first request filter; current is just a copy of V18 + switch ($model) { + case 'functions.create': + $content['something'] = $content['somethingElse'] ?? ""; + unset($content['something']); + break; } + */ return $content; } diff --git a/src/Appwrite/Utopia/Response/Model/Webhook.php b/src/Appwrite/Utopia/Response/Model/Webhook.php index 57abb4900d..8e53b41e6e 100644 --- a/src/Appwrite/Utopia/Response/Model/Webhook.php +++ b/src/Appwrite/Utopia/Response/Model/Webhook.php @@ -49,7 +49,7 @@ class Webhook extends Model 'type' => self::TYPE_STRING, 'description' => 'Webhook trigger events.', 'default' => [], - 'example' => 'database.collections.update', + 'example' => 'database.tables.update', 'array' => true, ]) ->addRule('security', [ From f94ecc885a4c4852f90f1c2ceac2d1c5d6adea58 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 27 Apr 2025 10:36:21 +0530 Subject: [PATCH 006/173] fix: tests as per new events system. --- tests/e2e/Services/Realtime/RealtimeBase.php | 6 +- .../Realtime/RealtimeConsoleClientTest.php | 98 ++++----- .../Realtime/RealtimeCustomClientTest.php | 202 +++++++++--------- tests/e2e/Services/Webhooks/WebhooksBase.php | 92 ++++---- .../Webhooks/WebhooksCustomServerTest.php | 40 ++-- tests/unit/Event/EventTest.php | 78 +++---- tests/unit/Event/MockPublisher.php | 2 +- .../Event/Validator/EventValidatorTest.php | 44 ++-- .../Validator/FunctionEventValidatorTest.php | 44 ++-- tests/unit/Messaging/MessagingTest.php | 38 ++-- 10 files changed, 322 insertions(+), 322 deletions(-) diff --git a/tests/e2e/Services/Realtime/RealtimeBase.php b/tests/e2e/Services/Realtime/RealtimeBase.php index 99f31134c0..0004781773 100644 --- a/tests/e2e/Services/Realtime/RealtimeBase.php +++ b/tests/e2e/Services/Realtime/RealtimeBase.php @@ -42,7 +42,7 @@ trait RealtimeBase /** * Test for SUCCESS */ - $client = $this->getWebsocket(["documents"]); + $client = $this->getWebsocket(["rows"]); $this->assertNotEmpty($client->receive()); $client->close(); } @@ -58,7 +58,7 @@ trait RealtimeBase $this->assertEquals(1008, $payload["data"]["code"]); $this->assertEquals("Missing channels", $payload["data"]["message"]); \usleep(250000); // 250ms - $this->expectException(ConnectionException::class); // Check if server disconnnected client + $this->expectException(ConnectionException::class); // Check if server disconnected client $client->close(); } @@ -83,7 +83,7 @@ trait RealtimeBase $payload["data"]["message"] ); \usleep(250000); // 250ms - $this->expectException(ConnectionException::class); // Check if server disconnnected client + $this->expectException(ConnectionException::class); // Check if server disconnected client $client->close(); } } diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index 4e27457b05..a206692ea2 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -201,12 +201,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -222,12 +222,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -292,12 +292,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); $this->assertEquals('processing', $response['data']['payload']['status']); @@ -311,12 +311,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); $this->assertEquals('available', $response['data']['payload']['status']); @@ -372,12 +372,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); /** Delete index generates two events. One from the API and one from the database worker */ @@ -390,12 +390,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); $client->close(); @@ -449,12 +449,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); $response = json_decode($client->receive(), true); @@ -467,12 +467,12 @@ class RealtimeConsoleClientTest extends Scope $this->assertCount(2, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); $this->assertContains("projects.{$projectId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.columns.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); $client->close(); @@ -607,7 +607,7 @@ class RealtimeConsoleClientTest extends Scope $this->assertContains("projects.{$projectId}", $response['data']['channels']); $this->assertArrayHasKey('buildLogs', $response['data']['payload']); - // Ignore comparasion for first payload + // Ignore comparison for first payload if ($previousBuildLogs !== null) { $this->assertNotEquals($previousBuildLogs, $response['data']['payload']['buildLogs']); } diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index e356397408..d279bb45c7 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -31,7 +31,7 @@ class RealtimeCustomClientTest extends Scope 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session ]; - $client = $this->getWebsocket(['documents'], $headers); + $client = $this->getWebsocket(['rows'], $headers); $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); @@ -40,7 +40,7 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertNotEmpty($response['data']['user']); $this->assertCount(1, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); + $this->assertContains('rows', $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); $client->close(); @@ -60,7 +60,7 @@ class RealtimeCustomClientTest extends Scope $client->close(); - $client = $this->getWebsocket(['account', 'documents', 'account.123'], $headers); + $client = $this->getWebsocket(['account', 'rows', 'account.123'], $headers); $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); @@ -69,7 +69,7 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertNotEmpty($response['data']['user']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); + $this->assertContains('rows', $response['data']['channels']); $this->assertContains('account', $response['data']['channels']); $this->assertContains('account.' . $userId, $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); @@ -80,12 +80,12 @@ class RealtimeCustomClientTest extends Scope 'account', 'files', 'files.1', - 'collections', - 'collections.1.documents', - 'collections.2.documents', - 'documents', - 'collections.1.documents.1', - 'collections.2.documents.2', + 'tables', + 'tables.1.rows', + 'tables.2.rows', + 'rows', + 'tables.1.rows.1', + 'tables.2.rows.2', ], $headers); $response = json_decode($client->receive(), true); @@ -100,12 +100,12 @@ class RealtimeCustomClientTest extends Scope $this->assertContains('account.' . $userId, $response['data']['channels']); $this->assertContains('files', $response['data']['channels']); $this->assertContains('files.1', $response['data']['channels']); - $this->assertContains('collections', $response['data']['channels']); - $this->assertContains('collections.1.documents', $response['data']['channels']); - $this->assertContains('collections.2.documents', $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains('collections.1.documents.1', $response['data']['channels']); - $this->assertContains('collections.2.documents.2', $response['data']['channels']); + $this->assertContains('tables', $response['data']['channels']); + $this->assertContains('tables.1.rows', $response['data']['channels']); + $this->assertContains('tables.2.rows', $response['data']['channels']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains('tables.1.rows.1', $response['data']['channels']); + $this->assertContains('tables.2.rows.2', $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); $client->close(); @@ -245,7 +245,7 @@ class RealtimeCustomClientTest extends Scope /** * Test for FAILURE */ - $client = $this->getWebsocket(['documents'], ['origin' => 'http://appwrite.unknown']); + $client = $this->getWebsocket(['rows'], ['origin' => 'http://appwrite.unknown']); $payload = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $payload); @@ -675,7 +675,7 @@ class RealtimeCustomClientTest extends Scope $session = $user['session'] ?? ''; $projectId = $this->getProject()['$id']; - $client = $this->getWebsocket(['documents', 'collections'], [ + $client = $this->getWebsocket(['rows', 'tables'], [ 'origin' => 'http://localhost', 'cookie' => 'a_session_' . $projectId . '=' . $session ]); @@ -687,8 +687,8 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('connected', $response['type']); $this->assertNotEmpty($response['data']); $this->assertCount(2, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains('collections', $response['data']['channels']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains('tables', $response['data']['channels']); $this->assertNotEmpty($response['data']['user']); $this->assertEquals($user['$id'], $response['data']['user']['$id']); @@ -770,19 +770,19 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents.' . $documentId, $response['data']['channels']); - $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows.' . $documentId, $response['data']['channels']); + $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -814,19 +814,19 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -868,19 +868,19 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -895,7 +895,7 @@ class RealtimeCustomClientTest extends Scope $session = $user['session'] ?? ''; $projectId = $this->getProject()['$id']; - $client = $this->getWebsocket(['documents', 'collections'], [ + $client = $this->getWebsocket(['rows', 'tables'], [ 'origin' => 'http://localhost', 'cookie' => 'a_session_' . $projectId . '=' . $session ]); @@ -907,8 +907,8 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('connected', $response['type']); $this->assertNotEmpty($response['data']); $this->assertCount(2, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains('collections', $response['data']['channels']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains('tables', $response['data']['channels']); $this->assertNotEmpty($response['data']['user']); $this->assertEquals($user['$id'], $response['data']['user']['$id']); @@ -988,19 +988,19 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -1027,19 +1027,19 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -1077,19 +1077,19 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('documents', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains('rows', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index c743810feb..5cd95c220d 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -83,10 +83,10 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -155,12 +155,12 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -181,12 +181,12 @@ trait WebhooksBase // $this->assertEquals($webhook['method'], 'DELETE'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -234,16 +234,16 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -296,16 +296,16 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -366,16 +366,16 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index d2f132e960..14188a807b 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -48,10 +48,10 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -96,12 +96,12 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -121,12 +121,12 @@ class WebhooksCustomServerTest extends Scope // $this->assertEquals($webhook['method'], 'DELETE'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -189,10 +189,10 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index c852cf2757..d5936e4b8f 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -93,57 +93,57 @@ class EventTest extends TestCase $this->assertContains('users.*.update', $event); $this->assertContains('users.*', $event); - $event = Event::generateEvents('collections.[collectionId].documents.[documentId].create', [ - 'collectionId' => 'chapters', - 'documentId' => 'prolog', + $event = Event::generateEvents('tables.[tableId].rows.[rowId].create', [ + 'tableId' => 'chapters', + 'rowId' => 'prolog', ]); $this->assertCount(10, $event); - $this->assertContains('collections.chapters.documents.prolog.create', $event); - $this->assertContains('collections.chapters.documents.prolog', $event); - $this->assertContains('collections.chapters.documents.*.create', $event); - $this->assertContains('collections.chapters.documents.*', $event); - $this->assertContains('collections.chapters', $event); - $this->assertContains('collections.*.documents.prolog.create', $event); - $this->assertContains('collections.*.documents.prolog', $event); - $this->assertContains('collections.*.documents.*.create', $event); - $this->assertContains('collections.*.documents.*', $event); - $this->assertContains('collections.*', $event); + $this->assertContains('tables.chapters.rows.prolog.create', $event); + $this->assertContains('tables.chapters.rows.prolog', $event); + $this->assertContains('tables.chapters.rows.*.create', $event); + $this->assertContains('tables.chapters.rows.*', $event); + $this->assertContains('tables.chapters', $event); + $this->assertContains('tables.*.rows.prolog.create', $event); + $this->assertContains('tables.*.rows.prolog', $event); + $this->assertContains('tables.*.rows.*.create', $event); + $this->assertContains('tables.*.rows.*', $event); + $this->assertContains('tables.*', $event); - $event = Event::generateEvents('databases.[databaseId].collections.[collectionId].documents.[documentId].create', [ + $event = Event::generateEvents('databases.[databaseId].tables.[tableId].rows.[rowId].create', [ 'databaseId' => 'chaptersDB', - 'collectionId' => 'chapters', - 'documentId' => 'prolog', + 'tableId' => 'chapters', + 'rowId' => 'prolog', ]); $this->assertCount(22, $event); - $this->assertContains('databases.chaptersDB.collections.chapters.documents.prolog.create', $event); - $this->assertContains('databases.chaptersDB.collections.chapters.documents.prolog', $event); - $this->assertContains('databases.chaptersDB.collections.chapters.documents.*.create', $event); - $this->assertContains('databases.chaptersDB.collections.chapters.documents.*', $event); - $this->assertContains('databases.chaptersDB.collections.chapters', $event); - $this->assertContains('databases.chaptersDB.collections.*.documents.prolog.create', $event); - $this->assertContains('databases.chaptersDB.collections.*.documents.prolog', $event); - $this->assertContains('databases.chaptersDB.collections.*', $event); + $this->assertContains('databases.chaptersDB.tables.chapters.rows.prolog.create', $event); + $this->assertContains('databases.chaptersDB.tables.chapters.rows.prolog', $event); + $this->assertContains('databases.chaptersDB.tables.chapters.rows.*.create', $event); + $this->assertContains('databases.chaptersDB.tables.chapters.rows.*', $event); + $this->assertContains('databases.chaptersDB.tables.chapters', $event); + $this->assertContains('databases.chaptersDB.tables.*.rows.prolog.create', $event); + $this->assertContains('databases.chaptersDB.tables.*.rows.prolog', $event); + $this->assertContains('databases.chaptersDB.tables.*', $event); $this->assertContains('databases.chaptersDB', $event); - $this->assertContains('databases.*.collections.chapters.documents.prolog.create', $event); - $this->assertContains('databases.*.collections.chapters.documents.prolog', $event); - $this->assertContains('databases.*.collections.chapters', $event); - $this->assertContains('databases.*.collections.*.documents.*.create', $event); - $this->assertContains('databases.*.collections.*.documents.*', $event); - $this->assertContains('databases.*.collections.*', $event); + $this->assertContains('databases.*.tables.chapters.rows.prolog.create', $event); + $this->assertContains('databases.*.tables.chapters.rows.prolog', $event); + $this->assertContains('databases.*.tables.chapters', $event); + $this->assertContains('databases.*.tables.*.rows.*.create', $event); + $this->assertContains('databases.*.tables.*.rows.*', $event); + $this->assertContains('databases.*.tables.*', $event); $this->assertContains('databases.*', $event); - $this->assertContains('databases.*.collections.*.documents.prolog', $event); - $this->assertContains('databases.*.collections.*.documents.prolog.create', $event); - $this->assertContains('databases.*.collections.chapters.documents.*', $event); - $this->assertContains('databases.*.collections.chapters.documents.*.create', $event); - $this->assertContains('databases.chaptersDB.collections.*.documents.*', $event); - $this->assertContains('databases.chaptersDB.collections.*.documents.*.create', $event); + $this->assertContains('databases.*.tables.*.rows.prolog', $event); + $this->assertContains('databases.*.tables.*.rows.prolog.create', $event); + $this->assertContains('databases.*.tables.chapters.rows.*', $event); + $this->assertContains('databases.*.tables.chapters.rows.*.create', $event); + $this->assertContains('databases.chaptersDB.tables.*.rows.*', $event); + $this->assertContains('databases.chaptersDB.tables.*.rows.*.create', $event); try { - $event = Event::generateEvents('collections.[collectionId].documents.[documentId].create', [ - 'collectionId' => 'chapters' + $event = Event::generateEvents('tables.[tableId].rows.[rowId].create', [ + 'tableId' => 'chapters' ]); $this->fail(); } catch (\Throwable $th) { @@ -151,7 +151,7 @@ class EventTest extends TestCase } try { - $event = Event::generateEvents('collections.[collectionId].documents.[documentId].create'); + $event = Event::generateEvents('tables.[tableId].rows.[rowId].create'); $this->fail(); } catch (\Throwable $th) { $this->assertInstanceOf(InvalidArgumentException::class, $th, 'An invalid exception was thrown'); diff --git a/tests/unit/Event/MockPublisher.php b/tests/unit/Event/MockPublisher.php index 54fcc89358..0b812e7032 100644 --- a/tests/unit/Event/MockPublisher.php +++ b/tests/unit/Event/MockPublisher.php @@ -7,7 +7,7 @@ use Utopia\Queue\Queue; class MockPublisher implements Publisher { - private $events = []; + private array $events = []; public function enqueue(Queue $queue, array $payload): bool { diff --git a/tests/unit/Event/Validator/EventValidatorTest.php b/tests/unit/Event/Validator/EventValidatorTest.php index e9f652adeb..6885d20bfb 100644 --- a/tests/unit/Event/Validator/EventValidatorTest.php +++ b/tests/unit/Event/Validator/EventValidatorTest.php @@ -29,27 +29,27 @@ class EventValidatorTest extends TestCase $this->assertTrue($this->object->isValid('users.*.update.email')); $this->assertTrue($this->object->isValid('users.*.update')); $this->assertTrue($this->object->isValid('users.*')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.*')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.*')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.*')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.*')); - $this->assertTrue($this->object->isValid('databases.*.collections.*')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.*')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.*')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.*')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.*')); + $this->assertTrue($this->object->isValid('databases.*.tables.*')); $this->assertTrue($this->object->isValid('databases.*')); $this->assertTrue($this->object->isValid('databases.books')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters')); - $this->assertTrue($this->object->isValid('databases.books.collections.*')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters')); + $this->assertTrue($this->object->isValid('databases.books.tables.*')); $this->assertTrue($this->object->isValid('functions.*')); $this->assertTrue($this->object->isValid('buckets.*')); $this->assertTrue($this->object->isValid('teams.*')); @@ -63,9 +63,9 @@ class EventValidatorTest extends TestCase $this->assertFalse($this->object->isValid(null)); $this->assertFalse($this->object->isValid('')); $this->assertFalse($this->object->isValid('unknown.*')); - $this->assertFalse($this->object->isValid('collections')); - $this->assertFalse($this->object->isValid('collections.*.unknown')); - $this->assertFalse($this->object->isValid('collections.*.documents.*.unknown')); + $this->assertFalse($this->object->isValid('tables')); + $this->assertFalse($this->object->isValid('tables.*.unknown')); + $this->assertFalse($this->object->isValid('tables.*.rows.*.unknown')); $this->assertFalse($this->object->isValid('users.torsten.unknown')); $this->assertFalse($this->object->isValid('users.torsten.delete.email')); $this->assertFalse($this->object->isValid('teams.*.memberships.*.update.unknown')); diff --git a/tests/unit/Event/Validator/FunctionEventValidatorTest.php b/tests/unit/Event/Validator/FunctionEventValidatorTest.php index ea59f6771a..9b6e05b9f7 100644 --- a/tests/unit/Event/Validator/FunctionEventValidatorTest.php +++ b/tests/unit/Event/Validator/FunctionEventValidatorTest.php @@ -29,27 +29,27 @@ class FunctionEventValidatorTest extends TestCase $this->assertTrue($this->object->isValid('users.*.update.email')); $this->assertTrue($this->object->isValid('users.*.update')); $this->assertTrue($this->object->isValid('users.*')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters.documents.*')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.books.collections.*.documents.*')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.chapters.documents.*')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.prolog.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.prolog')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.*.create')); - $this->assertTrue($this->object->isValid('databases.*.collections.*.documents.*')); - $this->assertTrue($this->object->isValid('databases.*.collections.*')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters.rows.*')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.books.tables.*.rows.*')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.chapters.rows.*')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.prolog.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.prolog')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.*.create')); + $this->assertTrue($this->object->isValid('databases.*.tables.*.rows.*')); + $this->assertTrue($this->object->isValid('databases.*.tables.*')); $this->assertTrue($this->object->isValid('databases.*')); $this->assertTrue($this->object->isValid('databases.books')); - $this->assertTrue($this->object->isValid('databases.books.collections.chapters')); - $this->assertTrue($this->object->isValid('databases.books.collections.*')); + $this->assertTrue($this->object->isValid('databases.books.tables.chapters')); + $this->assertTrue($this->object->isValid('databases.books.tables.*')); $this->assertTrue($this->object->isValid('buckets.*')); $this->assertTrue($this->object->isValid('teams.*')); $this->assertTrue($this->object->isValid('users.*')); @@ -62,9 +62,9 @@ class FunctionEventValidatorTest extends TestCase $this->assertFalse($this->object->isValid(null)); $this->assertFalse($this->object->isValid('')); $this->assertFalse($this->object->isValid('unknown.*')); - $this->assertFalse($this->object->isValid('collections')); - $this->assertFalse($this->object->isValid('collections.*.unknown')); - $this->assertFalse($this->object->isValid('collections.*.documents.*.unknown')); + $this->assertFalse($this->object->isValid('tables')); + $this->assertFalse($this->object->isValid('tables.*.unknown')); + $this->assertFalse($this->object->isValid('tables.*.rows.*.unknown')); $this->assertFalse($this->object->isValid('users.torsten.unknown')); $this->assertFalse($this->object->isValid('users.torsten.delete.email')); $this->assertFalse($this->object->isValid('teams.*.memberships.*.update.unknown')); diff --git a/tests/unit/Messaging/MessagingTest.php b/tests/unit/Messaging/MessagingTest.php index c2b6490869..2cb15c5750 100644 --- a/tests/unit/Messaging/MessagingTest.php +++ b/tests/unit/Messaging/MessagingTest.php @@ -35,7 +35,7 @@ class MessagingTest extends TestCase Role::team(ID::custom('def'))->toString(), Role::team(ID::custom('def'), 'guest')->toString(), ], - ['files' => 0, 'documents' => 0, 'documents.789' => 0, 'account.123' => 0] + ['files' => 0, 'rows' => 0, 'rows.789' => 0, 'account.123' => 0] ); $event = [ @@ -115,13 +115,13 @@ class MessagingTest extends TestCase $this->assertEmpty($receivers); $event['roles'] = [Role::any()->toString()]; - $event['data']['channels'] = ['documents.123']; + $event['data']['channels'] = ['rows.123']; $receivers = $realtime->getSubscribers($event); $this->assertEmpty($receivers); - $event['data']['channels'] = ['documents.789']; + $event['data']['channels'] = ['rows.789']; $receivers = $realtime->getSubscribers($event); @@ -153,8 +153,8 @@ class MessagingTest extends TestCase $channels = [ 0 => 'files', - 1 => 'documents', - 2 => 'documents.789', + 1 => 'rows', + 2 => 'rows.789', 3 => 'account', 4 => 'account.456' ]; @@ -162,8 +162,8 @@ class MessagingTest extends TestCase $channels = Realtime::convertChannels($channels, $user->getId()); $this->assertCount(4, $channels); $this->assertArrayHasKey('files', $channels); - $this->assertArrayHasKey('documents', $channels); - $this->assertArrayHasKey('documents.789', $channels); + $this->assertArrayHasKey('rows', $channels); + $this->assertArrayHasKey('rows.789', $channels); $this->assertArrayHasKey('account', $channels); $this->assertArrayNotHasKey('account.456', $channels); } @@ -190,8 +190,8 @@ class MessagingTest extends TestCase ]); $channels = [ 0 => 'files', - 1 => 'documents', - 2 => 'documents.789', + 1 => 'rows', + 2 => 'rows.789', 3 => 'account', 4 => 'account.456' ]; @@ -200,8 +200,8 @@ class MessagingTest extends TestCase $this->assertCount(5, $channels); $this->assertArrayHasKey('files', $channels); - $this->assertArrayHasKey('documents', $channels); - $this->assertArrayHasKey('documents.789', $channels); + $this->assertArrayHasKey('rows', $channels); + $this->assertArrayHasKey('rows.789', $channels); $this->assertArrayHasKey('account.123', $channels); $this->assertArrayHasKey('account', $channels); $this->assertArrayNotHasKey('account.456', $channels); @@ -213,10 +213,10 @@ class MessagingTest extends TestCase * Test Collection Level Permissions */ $result = Realtime::fromPayload( - event: 'databases.database_id.collections.collection_id.documents.document_id.create', + event: 'databases.database_id.tables.collection_id.rows.document_id.create', payload: new Document([ '$id' => ID::custom('test'), - '$collection' => ID::custom('collection'), + '$collection' => ID::custom('table'), '$permissions' => [ Permission::read(Role::team('123abc')), Permission::update(Role::team('123abc')), @@ -226,8 +226,8 @@ class MessagingTest extends TestCase database: new Document([ '$id' => ID::custom('database'), ]), - collection: new Document([ - '$id' => ID::custom('collection'), + table: new Document([ + '$id' => ID::custom('table'), '$permissions' => [ Permission::read(Role::any()), Permission::update(Role::any()), @@ -243,10 +243,10 @@ class MessagingTest extends TestCase * Test Document Level Permissions */ $result = Realtime::fromPayload( - event: 'databases.database_id.collections.collection_id.documents.document_id.create', + event: 'databases.database_id.tables.collection_id.rows.document_id.create', payload: new Document([ '$id' => ID::custom('test'), - '$collection' => ID::custom('collection'), + '$collection' => ID::custom('table'), '$permissions' => [ Permission::read(Role::any()), Permission::update(Role::any()), @@ -256,8 +256,8 @@ class MessagingTest extends TestCase database: new Document([ '$id' => ID::custom('database'), ]), - collection: new Document([ - '$id' => ID::custom('collection'), + table: new Document([ + '$id' => ID::custom('table'), '$permissions' => [ Permission::read(Role::team('123abc')), Permission::update(Role::team('123abc')), From 9cccd89439431736ead2620732fe9fd64fda9048 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 27 Apr 2025 14:08:26 +0530 Subject: [PATCH 007/173] fix: graphql tests. --- tests/e2e/Services/GraphQL/AbuseTest.php | 6 +- tests/e2e/Services/GraphQL/AuthTest.php | 53 +-- tests/e2e/Services/GraphQL/Base.php | 302 +++++++++--------- .../Services/GraphQL/DatabaseClientTest.php | 2 +- .../Services/GraphQL/DatabaseServerTest.php | 287 ++++++++--------- 5 files changed, 326 insertions(+), 324 deletions(-) diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/AbuseTest.php index ea97492c2b..7137123757 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/AbuseTest.php @@ -32,7 +32,7 @@ class AbuseTest extends Scope $databaseId = $data['databaseId']; $collectionId = $data['collectionId']; $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DOCUMENT); + $query = $this->getQuery(self::$CREATE_ROW); $max = 120; for ($i = 0; $i <= $max + 1; $i++) { @@ -133,7 +133,7 @@ class AbuseTest extends Scope $databaseId = $response['body']['data']['databasesCreate']['_id']; - $query = $this->getQuery(self::$CREATE_COLLECTION); + $query = $this->getQuery(self::$CREATE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -156,7 +156,7 @@ class AbuseTest extends Scope $collectionId = $response['body']['data']['databasesCreateCollection']['_id']; - $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ diff --git a/tests/e2e/Services/GraphQL/AuthTest.php b/tests/e2e/Services/GraphQL/AuthTest.php index ecce29f2b3..d8bd0d6326 100644 --- a/tests/e2e/Services/GraphQL/AuthTest.php +++ b/tests/e2e/Services/GraphQL/AuthTest.php @@ -23,7 +23,7 @@ class AuthTest extends Scope private string $token2; private array $database; - private array $collection; + private array $table; public function setUp(): void { @@ -101,7 +101,7 @@ class AuthTest extends Scope ], $gqlPayload); // Create collection - $query = $this->getQuery(self::$CREATE_COLLECTION); + $query = $this->getQuery(self::$CREATE_TABLE); $userId = $this->account1['body']['data']['accountCreate']['_id']; $gqlPayload = [ 'query' => $query, @@ -115,19 +115,19 @@ class AuthTest extends Scope ] ] ]; - $this->collection = $this->client->call(Client::METHOD_POST, '/graphql', [ + $this->table = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); // Create string attribute - $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -147,13 +147,13 @@ class AuthTest extends Scope $projectId = $this->getProject()['$id']; // Create document as account 1 - $query = $this->getQuery(self::$CREATE_DOCUMENT); + $query = $this->getQuery(self::$CREATE_ROW); $userId = $this->account1['body']['data']['accountCreate']['_id']; $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', @@ -165,40 +165,41 @@ class AuthTest extends Scope ] ] ]; - $document = $this->client->call(Client::METHOD_POST, '/graphql', [ + + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); // Try to read as account 1 - $query = $this->getQuery(self::$GET_DOCUMENT); + $query = $this->getQuery(self::$GET_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], - 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], + 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'documentId' => $row['body']['data']['databasesCreateRow']['_id'], ] ]; - $document = $this->client->call(Client::METHOD_POST, '/graphql', [ + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); - $this->assertIsArray($document['body']['data']['databasesGetDocument']); - $this->assertArrayNotHasKey('errors', $document['body']); + $this->assertIsArray($row['body']['data']['databasesGetRow']); + $this->assertArrayNotHasKey('errors', $row['body']); // Try to read as account 2 - $document = $this->client->call(Client::METHOD_POST, '/graphql', [ + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token2, ], $gqlPayload); - $this->assertArrayHasKey('errors', $document['body']); - $this->assertEquals('Document with the requested ID could not be found.', $document['body']['errors'][0]['message']); + $this->assertArrayHasKey('errors', $row['body']); + $this->assertEquals('Document with the requested ID could not be found.', $row['body']['errors'][0]['message']); } public function testValidAuth() @@ -206,13 +207,13 @@ class AuthTest extends Scope $projectId = $this->getProject()['$id']; // Create document as account 1 - $query = $this->getQuery(self::$CREATE_DOCUMENT); + $query = $this->getQuery(self::$CREATE_ROW); $userId = $this->account1['body']['data']['accountCreate']['_id']; $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', @@ -224,29 +225,29 @@ class AuthTest extends Scope ], ] ]; - $document = $this->client->call(Client::METHOD_POST, '/graphql', [ + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); // Try to delete as account 1 - $query = $this->getQuery(self::$DELETE_DOCUMENT); + $query = $this->getQuery(self::$DELETE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], - 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], + 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'documentId' => $row['body']['data']['databasesCreateRow']['_id'], ] ]; - $document = $this->client->call(Client::METHOD_POST, '/graphql', [ + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); - $this->assertIsNotArray($document['body']); - $this->assertEquals(204, $document['headers']['status-code']); + $this->assertIsNotArray($row['body']); + $this->assertEquals(204, $row['headers']['status-code']); } } diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 121d40156e..95f446cf6f 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -13,49 +13,49 @@ trait Base public static string $GET_DATABASE = 'get_database'; public static string $UPDATE_DATABASE = 'update_database'; public static string $DELETE_DATABASE = 'delete_database'; - // Collections - public static string $CREATE_COLLECTION = 'create_collection'; - public static string $GET_COLLECTION = 'get_collection'; - public static string $GET_COLLECTIONS = 'list_collections'; - public static string $UPDATE_COLLECTION = 'update_collection'; - public static string $DELETE_COLLECTION = 'delete_collection'; - // Attributes - public static string $CREATE_STRING_ATTRIBUTE = 'create_string_attribute'; - public static string $CREATE_INTEGER_ATTRIBUTE = 'create_integer_attribute'; - public static string $CREATE_FLOAT_ATTRIBUTE = 'create_float_attribute'; - public static string $CREATE_BOOLEAN_ATTRIBUTE = 'create_boolean_attribute'; - public static string $CREATE_URL_ATTRIBUTE = 'create_url_attribute'; - public static string $CREATE_EMAIL_ATTRIBUTE = 'create_email_attribute'; - public static string $CREATE_IP_ATTRIBUTE = 'create_ip_attribute'; - public static string $CREATE_ENUM_ATTRIBUTE = 'create_enum_attribute'; - public static string $CREATE_DATETIME_ATTRIBUTE = 'create_datetime_attribute'; + // Tables + public static string $CREATE_TABLE = 'create_table'; + public static string $GET_TABLE = 'get_table'; + public static string $GET_TABLES = 'list_tables'; + public static string $UPDATE_TABLE = 'update_table'; + public static string $DELETE_TABLE = 'delete_table'; + // Columns + public static string $CREATE_STRING_COLUMN = 'create_string_column'; + public static string $CREATE_INTEGER_COLUMN = 'create_integer_column'; + public static string $CREATE_FLOAT_COLUMN = 'create_float_column'; + public static string $CREATE_BOOLEAN_COLUMN = 'create_boolean_column'; + public static string $CREATE_URL_COLUMN = 'create_url_column'; + public static string $CREATE_EMAIL_COLUMN = 'create_email_column'; + public static string $CREATE_IP_COLUMN = 'create_ip_column'; + public static string $CREATE_ENUM_COLUMN = 'create_enum_column'; + public static string $CREATE_DATETIME_COLUMN = 'create_datetime_column'; - public static string $CREATE_RELATIONSHIP_ATTRIBUTE = 'create_relationship_attribute'; - public static string $UPDATE_STRING_ATTRIBUTE = 'update_string_attribute'; - public static string $UPDATE_INTEGER_ATTRIBUTE = 'update_integer_attribute'; - public static string $UPDATE_FLOAT_ATTRIBUTE = 'update_float_attribute'; - public static string $UPDATE_BOOLEAN_ATTRIBUTE = 'update_boolean_attribute'; - public static string $UPDATE_URL_ATTRIBUTE = 'update_url_attribute'; - public static string $UPDATE_EMAIL_ATTRIBUTE = 'update_email_attribute'; - public static string $UPDATE_IP_ATTRIBUTE = 'update_ip_attribute'; - public static string $UPDATE_ENUM_ATTRIBUTE = 'update_enum_attribute'; - public static string $UPDATE_DATETIME_ATTRIBUTE = 'update_datetime_attribute'; + public static string $CREATE_RELATIONSHIP_COLUMN = 'create_relationship_column'; + public static string $UPDATE_STRING_COLUMN = 'update_string_column'; + public static string $UPDATE_INTEGER_COLUMN = 'update_integer_column'; + public static string $UPDATE_FLOAT_COLUMN = 'update_float_column'; + public static string $UPDATE_BOOLEAN_COLUMN = 'update_boolean_column'; + public static string $UPDATE_URL_COLUMN = 'update_url_column'; + public static string $UPDATE_EMAIL_COLUMN = 'update_email_column'; + public static string $UPDATE_IP_COLUMN = 'update_ip_column'; + public static string $UPDATE_ENUM_COLUMN = 'update_enum_column'; + public static string $UPDATE_DATETIME_COLUMN = 'update_datetime_column'; - public static string $UPDATE_RELATIONSHIP_ATTRIBUTE = 'update_relationship_attribute'; - public static string $GET_ATTRIBUTES = 'get_attributes'; - public static string $GET_ATTRIBUTE = 'get_attribute'; - public static string $DELETE_ATTRIBUTE = 'delete_attribute'; + public static string $UPDATE_RELATIONSHIP_COLUMN = 'update_relationship_column'; + public static string $GET_COLUMNS = 'get_columns'; + public static string $GET_COLUMN = 'get_column'; + public static string $DELETE_COLUMN = 'delete_column'; // Indexes public static string $CREATE_INDEX = 'create_index'; public static string $GET_INDEXES = 'get_indexes'; public static string $GET_INDEX = 'get_index'; public static string $DELETE_INDEX = 'delete_index'; // Documents - public static string $CREATE_DOCUMENT = 'create_document_rest'; - public static string $GET_DOCUMENTS = 'list_documents'; - public static string $GET_DOCUMENT = 'get_document'; - public static string $UPDATE_DOCUMENT = 'update_document'; - public static string $DELETE_DOCUMENT = 'delete_document'; + public static string $CREATE_ROW = 'create_row_rest'; + public static string $GET_ROWS = 'list_rows'; + public static string $GET_ROW = 'get_row'; + public static string $UPDATE_ROW = 'update_row'; + public static string $DELETE_ROW = 'delete_row'; // Custom Entities public static string $CREATE_CUSTOM_ENTITY = 'create_custom_entity'; @@ -258,7 +258,7 @@ trait Base public static string $COMPLEX_QUERY = 'complex_query'; // Fragments - public static string $FRAGMENT_ATTRIBUTES = ' + public static string $FRAGMENT_COLUMNS = ' fragment attributeProperties on Attributes { ... on AttributeString { key @@ -393,18 +393,18 @@ trait Base status } }'; - case self::$GET_COLLECTION: - return 'query getCollection($databaseId: String!, $collectionId: String!) { - databasesGetCollection(databaseId: $databaseId, collectionId: $collectionId) { + case self::$GET_TABLE: + return 'query getTable($databaseId: String!, $tableId: String!) { + databasesGetTable(databaseId: $databaseId, tableId: $tableId) { _id _permissions documentSecurity name } }'; - case self::$GET_COLLECTIONS: - return 'query listCollections($databaseId: String!) { - databasesListCollections(databaseId: $databaseId) { + case self::$GET_TABLES: + return 'query listTables($databaseId: String!) { + databasesListTables(databaseId: $databaseId) { total collections { _id @@ -414,42 +414,42 @@ trait Base } } }'; - case self::$CREATE_COLLECTION: - return 'mutation createCollection($databaseId: String!, $collectionId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!]!) { - databasesCreateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions) { + case self::$CREATE_TABLE: + return 'mutation createTable($databaseId: String!, $tableId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!]!) { + databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions) { _id _permissions documentSecurity name } }'; - case self::$UPDATE_COLLECTION: - return 'mutation updateCollection($databaseId: String!, $collectionId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!], $enabled: Boolean){ - databasesUpdateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions, enabled: $enabled) { + case self::$UPDATE_TABLE: + return 'mutation updateTable($databaseId: String!, $tableId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!], $enabled: Boolean){ + databasesUpdateTable(databaseId: $databaseId, tableId: $tableId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions, enabled: $enabled) { _id _permissions documentSecurity name } }'; - case self::$DELETE_COLLECTION: - return 'mutation deleteCollection($databaseId: String!, $collectionId: String!){ - databasesDeleteCollection(databaseId: $databaseId, collectionId: $collectionId) { + case self::$DELETE_TABLE: + return 'mutation deleteTable($databaseId: String!, $tableId: String!){ + databasesDeleteTable(databaseId: $databaseId, tableId: $tableId) { status } }'; - case self::$CREATE_STRING_ATTRIBUTE: - return 'mutation createStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { + case self::$CREATE_STRING_COLUMN: + return 'mutation createStringColumn($databaseId: String!, $tableId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateStringColumn(databaseId: $databaseId, tableId: $tableId, key: $key, size: $size, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_INTEGER_ATTRIBUTE: - return 'mutation createIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ - databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + case self::$CREATE_INTEGER_COLUMN: + return 'mutation createIntegerColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ + databasesCreateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -458,9 +458,9 @@ trait Base array } }'; - case self::$CREATE_FLOAT_ATTRIBUTE: - return 'mutation createFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ - databasesCreateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + case self::$CREATE_FLOAT_COLUMN: + return 'mutation createFloatColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ + databasesCreateFloatColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -469,45 +469,45 @@ trait Base array } }'; - case self::$CREATE_BOOLEAN_ATTRIBUTE: - return 'mutation createBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ - databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_BOOLEAN_COLUMN: + return 'mutation createBooleanColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ + databasesCreateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_URL_ATTRIBUTE: - return 'mutation createUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_URL_COLUMN: + return 'mutation createUrlColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateUrlColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_EMAIL_ATTRIBUTE: - return 'mutation createEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_EMAIL_COLUMN: + return 'mutation createEmailColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateEmailColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_IP_ATTRIBUTE: - return 'mutation createIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_IP_COLUMN: + return 'mutation createIpColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateIpColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_ENUM_ATTRIBUTE: - return 'mutation createEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { + case self::$CREATE_ENUM_COLUMN: + return 'mutation createEnumColumn($databaseId: String!, $tableId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateEnumColumn(databaseId: $databaseId, tableId: $tableId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { key elements required @@ -515,18 +515,18 @@ trait Base array } }'; - case self::$CREATE_DATETIME_ATTRIBUTE: - return 'mutation createDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_DATETIME_COLUMN: + return 'mutation createDatetimeColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateDatetimeColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_RELATIONSHIP_ATTRIBUTE: - return 'mutation createRelationshipAttribute($databaseId: String!, $collectionId: String!, $relatedCollectionId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ - databasesCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { + case self::$CREATE_RELATIONSHIP_COLUMN: + return 'mutation createRelationshipColumn($databaseId: String!, $tableId: String!, $relatedTableId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ + databasesCreateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, relatedTableId: $relatedTableId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { relatedCollection relationType twoWay @@ -535,77 +535,77 @@ trait Base onDelete } }'; - case self::$UPDATE_STRING_ATTRIBUTE: - return 'mutation updateStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + case self::$UPDATE_STRING_COLUMN: + return 'mutation updateStringColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateStringColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_INTEGER_ATTRIBUTE: - return 'mutation updateIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ - databasesUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) { + case self::$UPDATE_INTEGER_COLUMN: + return 'mutation updateIntegerColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ + databasesUpdateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, min: $min, max: $max, default: $default) { required min max default } }'; - case self::$UPDATE_FLOAT_ATTRIBUTE: - return 'mutation updateFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ - databasesUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) { + case self::$UPDATE_FLOAT_COLUMN: + return 'mutation updateFloatColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ + databasesUpdateFloatColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default) { required min max default } }'; - case self::$UPDATE_BOOLEAN_ATTRIBUTE: - return 'mutation updateBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean){ - databasesUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + case self::$UPDATE_BOOLEAN_COLUMN: + return 'mutation updateBooleanColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: Boolean){ + databasesUpdateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_URL_ATTRIBUTE: - return 'mutation updateUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + case self::$UPDATE_URL_COLUMN: + return 'mutation updateUrlColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateUrlColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_EMAIL_ATTRIBUTE: - return 'mutation updateEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + case self::$UPDATE_EMAIL_COLUMN: + return 'mutation updateEmailColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateEmailColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_IP_ATTRIBUTE: - return 'mutation updateIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + case self::$UPDATE_IP_COLUMN: + return 'mutation updateIpColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateIpColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_ENUM_ATTRIBUTE: - return 'mutation updateEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ - databasesUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) { + case self::$UPDATE_ENUM_COLUMN: + return 'mutation updateEnumColumn($databaseId: String!, $tableId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ + databasesUpdateEnumColumn(databaseId: $databaseId, tableId: $tableId, key: $key, elements: $elements, required: $required, default: $default) { elements required default } }'; - case self::$UPDATE_DATETIME_ATTRIBUTE: - return 'mutation updateDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + case self::$UPDATE_DATETIME_COLUMN: + return 'mutation updateDatetimeColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateDatetimeColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_RELATIONSHIP_ATTRIBUTE: - return 'mutation updateRelationshipAttribute($databaseId: String!, $collectionId: String!, $key: String!, $onDelete: String){ - databasesUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) { + case self::$UPDATE_RELATIONSHIP_COLUMN: + return 'mutation updateRelationshipColumn($databaseId: String!, $tableId: String!, $key: String!, $onDelete: String){ + databasesUpdateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, key: $key, onDelete: $onDelete) { relatedCollection relationType twoWay @@ -615,16 +615,16 @@ trait Base } }'; case self::$CREATE_INDEX: - return 'mutation createIndex($databaseId: String!, $collectionId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ - databasesCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { + return 'mutation createIndex($databaseId: String!, $tableId: String!, $key: String!, $type: String!, $columns: [String!]!, $orders: [String!]){ + databasesCreateIndex(databaseId: $databaseId, tableId: $tableId, key: $key, type: $type, columns: $columns, orders: $orders) { key type status } }'; case self::$GET_INDEXES: - return 'query listIndexes($databaseId: String!, $collectionId: String!) { - databasesListIndexes(databaseId: $databaseId, collectionId: $collectionId) { + return 'query listIndexes($databaseId: String!, $tableId: String!) { + databasesListIndexes(databaseId: $databaseId, tableId: $tableId) { total indexes { key @@ -634,52 +634,52 @@ trait Base } }'; case self::$GET_INDEX: - return 'query getIndex($databaseId: String!, $collectionId: String!, $key: String!) { - databasesGetIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + return 'query getIndex($databaseId: String!, $tableId: String!, $key: String!) { + databasesGetIndex(databaseId: $databaseId, tableId: $tableId, key: $key) { key type status } }'; case self::$DELETE_INDEX: - return 'mutation deleteIndex($databaseId: String!, $collectionId: String!, $key: String!) { - databasesDeleteIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + return 'mutation deleteIndex($databaseId: String!, $tableId: String!, $key: String!) { + databasesDeleteIndex(databaseId: $databaseId, tableId: $tableId, key: $key) { status } }'; - case self::$GET_ATTRIBUTES: - return 'query listAttributes($databaseId: String!, $collectionId: String!) { - databasesListAttributes(databaseId: $databaseId, collectionId: $collectionId) { + case self::$GET_COLUMNS: + return 'query listColumns($databaseId: String!, $tableId: String!) { + databasesListColumns(databaseId: $databaseId, tableId: $tableId) { total attributes { ...attributeProperties } } - }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; - case self::$GET_ATTRIBUTE: - return 'query getAttribute($databaseId: String!, $collectionId: String!, $key: String!) { - databasesGetAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + }' . PHP_EOL . self::$FRAGMENT_COLUMNS; + case self::$GET_COLUMN: + return 'query getColumn($databaseId: String!, $tableId: String!, $key: String!) { + databasesGetColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { ...attributeProperties } - }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; - case self::$DELETE_ATTRIBUTE: - return 'mutation deleteAttribute($databaseId: String!, $collectionId: String!, $key: String!) { - databasesDeleteAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + }' . PHP_EOL . self::$FRAGMENT_COLUMNS; + case self::$DELETE_COLUMN: + return 'mutation deleteColumn($databaseId: String!, $tableId: String!, $key: String!) { + databasesDeleteColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { status } }'; - case self::$GET_DOCUMENT: - return 'query getDocument($databaseId: String!, $collectionId: String!, $documentId: String!) { - databasesGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { + case self::$GET_ROW: + return 'query getRow($databaseId: String!, $tableId: String!, $rowId: String!) { + databasesGetRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { _id _collectionId _permissions data } }'; - case self::$GET_DOCUMENTS: - return 'query listDocuments($databaseId: String!, $collectionId: String!){ - databasesListDocuments(databaseId: $databaseId, collectionId: $collectionId) { + case self::$GET_ROWS: + return 'query listRows($databaseId: String!, $tableId: String!){ + databasesListRows(databaseId: $databaseId, tableId: $tableId) { total documents { _id @@ -689,9 +689,9 @@ trait Base } } }'; - case self::$CREATE_DOCUMENT: - return 'mutation createDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ - databasesCreateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + case self::$CREATE_ROW: + return 'mutation createRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]){ + databasesCreateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { _id _collectionId _permissions @@ -756,17 +756,17 @@ trait Base return 'mutation deleteCustomEntity($id: String!){ actorsDelete(id: $id) }'; - case self::$UPDATE_DOCUMENT: - return 'mutation updateDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ - databasesUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + case self::$UPDATE_ROW: + return 'mutation updateRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]){ + databasesUpdateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { _id _collectionId data } }'; - case self::$DELETE_DOCUMENT: - return 'mutation deleteDocument($databaseId: String!, $collectionId: String!, $documentId: String!){ - databasesDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { + case self::$DELETE_ROW: + return 'mutation deleteRow($databaseId: String!, $tableId: String!, $rowId: String!){ + databasesDeleteRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { status } }'; @@ -2246,12 +2246,12 @@ trait Base } }'; case self::$COMPLEX_QUERY: - return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) { + return 'mutation complex($databaseId: String!, $databaseName: String!, $tableId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) { databasesCreate(databaseId: $databaseId, name: $databaseName) { _id name } - databasesCreateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $collectionName, documentSecurity: $documentSecurity, permissions: $collectionPermissions) { + databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $collectionName, documentSecurity: $documentSecurity, permissions: $collectionPermissions) { _id _createdAt _updatedAt @@ -2268,7 +2268,7 @@ trait Base status } } - databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "name", size: 255, required: true) { + databasesCreateStringColumn(databaseId: $databaseId, tableId: $tableId, key: "name", size: 255, required: true) { key type status @@ -2277,7 +2277,7 @@ trait Base default array } - databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "age", min: 0, max: 150, required: true) { + databasesCreateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: "age", min: 0, max: 150, required: true) { key type status @@ -2287,7 +2287,7 @@ trait Base default array } - databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "alive", required: false, default: true) { + databasesCreateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: "alive", required: false, default: true) { key type status @@ -2487,7 +2487,7 @@ trait Base data } } - }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; + }' . PHP_EOL . self::$FRAGMENT_COLUMNS; } throw new \InvalidArgumentException('Invalid query type'); diff --git a/tests/e2e/Services/GraphQL/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/DatabaseClientTest.php index 3853a3fc17..327b68224b 100644 --- a/tests/e2e/Services/GraphQL/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseClientTest.php @@ -48,7 +48,7 @@ class DatabaseClientTest extends Scope public function testCreateCollection($database): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_COLLECTION); + $query = $this->getQuery(self::$CREATE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ diff --git a/tests/e2e/Services/GraphQL/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/DatabaseServerTest.php index 87006a1bea..0ea86c03cf 100644 --- a/tests/e2e/Services/GraphQL/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseServerTest.php @@ -49,12 +49,12 @@ class DatabaseServerTest extends Scope public function testCreateCollection($database): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_COLLECTION); + $query = $this->getQuery(self::$CREATE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $database['_id'], - 'collectionId' => 'actors', + 'tableId' => 'actors', 'name' => 'Actors', 'documentSecurity' => false, 'permissions' => [ @@ -73,14 +73,14 @@ class DatabaseServerTest extends Scope $this->assertIsArray($collection['body']['data']); $this->assertArrayNotHasKey('errors', $collection['body']); - $collection = $collection['body']['data']['databasesCreateCollection']; + $collection = $collection['body']['data']['databasesCreateTable']; $this->assertEquals('Actors', $collection['name']); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $database['_id'], - 'collectionId' => 'movies', + 'tableId' => 'movies', 'name' => 'Movies', 'documentSecurity' => false, 'permissions' => [ @@ -92,20 +92,20 @@ class DatabaseServerTest extends Scope ] ]; - $collection2 = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + $table2 = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); - $this->assertIsArray($collection2['body']['data']); - $this->assertArrayNotHasKey('errors', $collection2['body']); - $collection2 = $collection2['body']['data']['databasesCreateCollection']; - $this->assertEquals('Movies', $collection2['name']); + $this->assertIsArray($table2['body']['data']); + $this->assertArrayNotHasKey('errors', $table2['body']); + $table2 = $table2['body']['data']['databasesCreateTable']; + $this->assertEquals('Movies', $table2['name']); return [ 'database' => $database, - 'collection' => $collection, - 'collection2' => $collection2, + 'table' => $collection, + 'table2' => $table2, ]; } @@ -116,12 +116,12 @@ class DatabaseServerTest extends Scope public function testCreateStringAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -135,7 +135,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateStringColumn']); return $data; } @@ -150,12 +150,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'name', 'required' => false, 'default' => 'Default Value', @@ -168,9 +168,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateStringAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateStringAttribute']['required']); - $this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateStringColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateStringColumn']['required']); + $this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -183,12 +183,12 @@ class DatabaseServerTest extends Scope public function testCreateIntegerAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_INTEGER_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'age', 'min' => 18, 'max' => 150, @@ -203,7 +203,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerColumn']); return $data; } @@ -218,12 +218,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_INTEGER_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'age', 'required' => false, 'min' => 12, @@ -238,11 +238,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateIntegerAttribute']['required']); - $this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerAttribute']['min']); - $this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerAttribute']['max']); - $this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateIntegerColumn']['required']); + $this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerColumn']['min']); + $this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerColumn']['max']); + $this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -255,12 +255,12 @@ class DatabaseServerTest extends Scope public function testCreateBooleanAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_BOOLEAN_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_BOOLEAN_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'alive', 'required' => true, ] @@ -273,7 +273,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateBooleanAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateBooleanColumn']); return $data; } @@ -288,12 +288,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_BOOLEAN_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_BOOLEAN_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'alive', 'required' => false, 'default' => true @@ -306,9 +306,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateBooleanAttribute']['required']); - $this->assertTrue($attribute['body']['data']['databasesUpdateBooleanAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateBooleanColumn']['required']); + $this->assertTrue($attribute['body']['data']['databasesUpdateBooleanColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -321,12 +321,12 @@ class DatabaseServerTest extends Scope public function testCreateFloatAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_FLOAT_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_FLOAT_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'salary', 'min' => 1000.0, 'max' => 999999.99, @@ -342,7 +342,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateFloatAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateFloatColumn']); return $data; } @@ -357,12 +357,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_FLOAT_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_FLOAT_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'salary', 'required' => false, 'min' => 100.0, @@ -377,11 +377,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateFloatAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateFloatAttribute']['required']); - $this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['min']); - $this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['max']); - $this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateFloatColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateFloatColumn']['required']); + $this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatColumn']['min']); + $this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatColumn']['max']); + $this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -394,12 +394,12 @@ class DatabaseServerTest extends Scope public function testCreateEmailAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_EMAIL_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_EMAIL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'email', 'required' => true, ] @@ -412,7 +412,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateEmailAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateEmailColumn']); return $data; } @@ -427,12 +427,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_EMAIL_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_EMAIL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'email', 'required' => false, 'default' => 'torsten@appwrite.io', @@ -445,9 +445,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateEmailAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateEmailAttribute']['required']); - $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateEmailColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateEmailColumn']['required']); + $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -460,12 +460,12 @@ class DatabaseServerTest extends Scope public function testCreateEnumAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_ENUM_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_ENUM_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'role', 'elements' => [ 'crew', @@ -483,7 +483,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateEnumAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateEnumColumn']); return $data; } @@ -499,12 +499,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_ENUM_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_ENUM_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'role', 'required' => false, 'elements' => [ @@ -522,11 +522,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateEnumAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateEnumAttribute']['required']); - $this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['default']); - $this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); - $this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateEnumColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateEnumColumn']['required']); + $this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumColumn']['default']); + $this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumColumn']['elements']); + $this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumColumn']['elements']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -539,12 +539,12 @@ class DatabaseServerTest extends Scope public function testCreateDatetimeAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DATETIME_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_DATETIME_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'dob', 'required' => true, ] @@ -557,7 +557,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateDatetimeAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateDatetimeColumn']); return $data; } @@ -572,12 +572,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_DATETIME_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_DATETIME_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'dob', 'required' => false, 'default' => '2000-01-01T00:00:00Z' @@ -590,9 +590,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeAttribute']['required']); - $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeColumn']['required']); + $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -604,13 +604,13 @@ class DatabaseServerTest extends Scope public function testCreateRelationshipAttribute(array $data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_RELATIONSHIP_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_RELATIONSHIP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection2']['_id'], // Movies - 'relatedCollectionId' => $data['collection']['_id'], // Actors + 'tableId' => $data['table2']['_id'], // Movies + 'relatedTableId' => $data['table']['_id'], // Actors 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'actors', @@ -623,9 +623,10 @@ class DatabaseServerTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); + $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipColumn']); return $data; } @@ -638,12 +639,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection2']['_id'], + 'tableId' => $data['table2']['_id'], 'key' => 'actors', 'onDelete' => Database::RELATION_MUTATE_CASCADE, ] @@ -656,7 +657,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipColumn']); return $data; } @@ -668,12 +669,12 @@ class DatabaseServerTest extends Scope public function testCreateIPAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_IP_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_IP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'ip', 'required' => false, 'default' => '::1', @@ -687,7 +688,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIpAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIpColumn']); return $data; } @@ -702,12 +703,12 @@ class DatabaseServerTest extends Scope sleep(3); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_IP_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_IP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'ip', 'required' => false, 'default' => '127.0.0.1' @@ -720,9 +721,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateIpAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateIpAttribute']['required']); - $this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateIpColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateIpColumn']['required']); + $this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -735,12 +736,12 @@ class DatabaseServerTest extends Scope public function testCreateURLAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_URL_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_URL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'url', 'required' => false, 'default' => 'https://appwrite.io', @@ -754,7 +755,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateUrlAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateUrlColumn']); return $data; } @@ -769,12 +770,12 @@ class DatabaseServerTest extends Scope sleep(3); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_URL_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_URL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'url', 'required' => false, 'default' => 'https://cloud.appwrite.io' @@ -787,9 +788,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateUrlAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateUrlAttribute']['required']); - $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateUrlColumn']); + $this->assertFalse($attribute['body']['data']['databasesUpdateUrlColumn']['required']); + $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlColumn']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); } @@ -806,10 +807,10 @@ class DatabaseServerTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'index', 'type' => 'key', - 'attributes' => [ + 'columns' => [ 'name', 'age', ], @@ -827,7 +828,7 @@ class DatabaseServerTest extends Scope return [ 'database' => $data['database'], - 'collection' => $data['collection'], + 'table' => $data['table'], 'index' => $index['body']['data']['databasesCreateIndex'], ]; } @@ -842,13 +843,13 @@ class DatabaseServerTest extends Scope public function testCreateDocument($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DOCUMENT); + $query = $this->getQuery(self::$CREATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => ID::unique(), + 'tableId' => $data['table']['_id'], + 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', 'email' => 'example@appwrite.io', @@ -866,21 +867,21 @@ class DatabaseServerTest extends Scope ] ]; - $document = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); - $this->assertArrayNotHasKey('errors', $document['body']); - $this->assertIsArray($document['body']['data']); + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); - $document = $document['body']['data']['databasesCreateDocument']; - $this->assertIsArray($document); + $row = $row['body']['data']['databasesCreateRow']; + $this->assertIsArray($row); return [ 'database' => $data['database'], - 'collection' => $data['collection'], - 'document' => $document, + 'table' => $data['table'], + 'row' => $row, ]; } @@ -974,7 +975,7 @@ class DatabaseServerTest extends Scope public function testGetCollections($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_COLLECTIONS); + $query = $this->getQuery(self::$GET_TABLES); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -989,7 +990,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $collections['body']); $this->assertIsArray($collections['body']['data']); - $this->assertIsArray($collections['body']['data']['databasesListCollections']); + $this->assertIsArray($collections['body']['data']['databasesListTables']); } /** @@ -999,12 +1000,12 @@ class DatabaseServerTest extends Scope public function testGetCollection($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_COLLECTION); + $query = $this->getQuery(self::$GET_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], ] ]; @@ -1015,7 +1016,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $collection['body']); $this->assertIsArray($collection['body']['data']); - $this->assertIsArray($collection['body']['data']['databasesGetCollection']); + $this->assertIsArray($collection['body']['data']['databasesGetTable']); } /** @@ -1026,12 +1027,12 @@ class DatabaseServerTest extends Scope public function testGetAttributes($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ATTRIBUTES); + $query = $this->getQuery(self::$GET_COLUMNS); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], ] ]; @@ -1042,7 +1043,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attributes['body']); $this->assertIsArray($attributes['body']['data']); - $this->assertIsArray($attributes['body']['data']['databasesListAttributes']); + $this->assertIsArray($attributes['body']['data']['databasesListColumns']); } /** @@ -1052,12 +1053,12 @@ class DatabaseServerTest extends Scope public function testGetAttribute($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ATTRIBUTE); + $query = $this->getQuery(self::$GET_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'name', ] ]; @@ -1069,7 +1070,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesGetAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesGetColumn']); } /** @@ -1084,7 +1085,7 @@ class DatabaseServerTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], ] ]; @@ -1110,7 +1111,7 @@ class DatabaseServerTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => $data['index']['key'], ] ]; @@ -1132,12 +1133,12 @@ class DatabaseServerTest extends Scope public function testGetDocuments($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_DOCUMENTS); + $query = $this->getQuery(self::$GET_ROWS); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], ] ]; @@ -1148,7 +1149,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['databasesListDocuments']); + $this->assertIsArray($documents['body']['data']['databasesListRows']); } /** @@ -1158,13 +1159,13 @@ class DatabaseServerTest extends Scope public function testGetDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_DOCUMENT); + $query = $this->getQuery(self::$GET_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => $data['document']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], ] ]; @@ -1175,7 +1176,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['databasesGetDocument']); + $this->assertIsArray($document['body']['data']['databasesGetRow']); } // /** @@ -1258,12 +1259,12 @@ class DatabaseServerTest extends Scope public function testUpdateCollection($data) { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_COLLECTION); + $query = $this->getQuery(self::$UPDATE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'name' => 'New Collection Name', 'documentSecurity' => false, ] @@ -1276,7 +1277,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $collection['body']); $this->assertIsArray($collection['body']['data']); - $this->assertIsArray($collection['body']['data']['databasesUpdateCollection']); + $this->assertIsArray($collection['body']['data']['databasesUpdateTable']); } /** @@ -1286,13 +1287,13 @@ class DatabaseServerTest extends Scope public function testUpdateDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_DOCUMENT); + $query = $this->getQuery(self::$UPDATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => $data['document']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], 'data' => [ 'name' => 'New Document Name', ], @@ -1306,7 +1307,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesUpdateDocument']; + $document = $document['body']['data']['databasesUpdateRow']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); } @@ -1346,13 +1347,13 @@ class DatabaseServerTest extends Scope public function testDeleteDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_DOCUMENT); + $query = $this->getQuery(self::$DELETE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => $data['document']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], ] ]; @@ -1396,12 +1397,12 @@ class DatabaseServerTest extends Scope public function testDeleteAttribute($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_ATTRIBUTE); + $query = $this->getQuery(self::$DELETE_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'name', ] ]; @@ -1422,12 +1423,12 @@ class DatabaseServerTest extends Scope public function testDeleteCollection($data) { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_COLLECTION); + $query = $this->getQuery(self::$DELETE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], ] ]; From 9bf1fdc2ff386ecb301551f993d62989b4840287 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 27 Apr 2025 14:27:07 +0530 Subject: [PATCH 008/173] fix: graphql tests. --- tests/e2e/Services/GraphQL/AuthTest.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/e2e/Services/GraphQL/AuthTest.php b/tests/e2e/Services/GraphQL/AuthTest.php index d8bd0d6326..48a2f475e6 100644 --- a/tests/e2e/Services/GraphQL/AuthTest.php +++ b/tests/e2e/Services/GraphQL/AuthTest.php @@ -107,7 +107,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'documentSecurity' => true, 'permissions' => [ @@ -127,7 +127,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -153,8 +153,8 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'documentId' => ID::unique(), + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', ], @@ -178,8 +178,8 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'documentId' => $row['body']['data']['databasesCreateRow']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => $row['body']['data']['databasesCreateRow']['_id'], ] ]; $row = $this->client->call(Client::METHOD_POST, '/graphql', [ @@ -213,8 +213,8 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'documentId' => ID::unique(), + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', ], @@ -237,8 +237,8 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'collectionId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'documentId' => $row['body']['data']['databasesCreateRow']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => $row['body']['data']['databasesCreateRow']['_id'], ] ]; $row = $this->client->call(Client::METHOD_POST, '/graphql', [ From 7ffd5cfde6c92e00cd4bf8cac0b19860aaebeefc Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 27 Apr 2025 14:47:59 +0530 Subject: [PATCH 009/173] fix: graphql tests, again. --- .../Services/GraphQL/DatabaseClientTest.php | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/e2e/Services/GraphQL/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/DatabaseClientTest.php index 327b68224b..efd2430629 100644 --- a/tests/e2e/Services/GraphQL/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseClientTest.php @@ -53,7 +53,7 @@ class DatabaseClientTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $database['_id'], - 'collectionId' => 'actors', + 'tableId' => 'actors', 'name' => 'Actors', 'documentSecurity' => false, 'permissions' => [ @@ -65,20 +65,20 @@ class DatabaseClientTest extends Scope ] ]; - $collection = $this->client->call(Client::METHOD_POST, '/graphql', [ + $table = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - $this->assertIsArray($collection['body']['data']); - $this->assertArrayNotHasKey('errors', $collection['body']); - $collection = $collection['body']['data']['databasesCreateCollection']; - $this->assertEquals('Actors', $collection['name']); + $this->assertIsArray($table['body']['data']); + $this->assertArrayNotHasKey('errors', $table['body']); + $table = $table['body']['data']['databasesCreateTable']; + $this->assertEquals('Actors', $table['name']); return [ 'database' => $database, - 'collection' => $collection, + 'table' => $table, ]; } @@ -88,12 +88,12 @@ class DatabaseClientTest extends Scope public function testCreateStringAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -108,7 +108,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateStringColumn']); return $data; } @@ -119,12 +119,12 @@ class DatabaseClientTest extends Scope public function testCreateIntegerAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_INTEGER_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], 'key' => 'age', 'min' => 18, 'max' => 150, @@ -140,7 +140,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerColumn']); return $data; } @@ -154,13 +154,13 @@ class DatabaseClientTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DOCUMENT); + $query = $this->getQuery(self::$CREATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => ID::unique(), + 'tableId' => $data['table']['_id'], + 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', 'age' => 35, @@ -181,13 +181,13 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesCreateDocument']; + $document = $document['body']['data']['databasesCreateRow']; $this->assertIsArray($document); return [ 'database' => $data['database'], - 'collection' => $data['collection'], - 'document' => $document, + 'table' => $data['table'], + 'row' => $document, ]; } @@ -198,12 +198,12 @@ class DatabaseClientTest extends Scope public function testGetDocuments($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_DOCUMENTS); + $query = $this->getQuery(self::$GET_ROWS); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], + 'tableId' => $data['table']['_id'], ] ]; @@ -214,7 +214,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['databasesListDocuments']); + $this->assertIsArray($documents['body']['data']['databasesListRows']); } /** @@ -224,13 +224,13 @@ class DatabaseClientTest extends Scope public function testGetDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_DOCUMENT); + $query = $this->getQuery(self::$GET_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => $data['document']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], ] ]; @@ -241,7 +241,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['databasesGetDocument']); + $this->assertIsArray($document['body']['data']['databasesGetRow']); } /** @@ -251,13 +251,13 @@ class DatabaseClientTest extends Scope public function testUpdateDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_DOCUMENT); + $query = $this->getQuery(self::$UPDATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => $data['document']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], 'data' => [ 'name' => 'New Document Name', ], @@ -271,7 +271,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesUpdateDocument']; + $document = $document['body']['data']['databasesUpdateRow']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); @@ -284,13 +284,13 @@ class DatabaseClientTest extends Scope public function testDeleteDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_DOCUMENT); + $query = $this->getQuery(self::$DELETE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'collectionId' => $data['collection']['_id'], - 'documentId' => $data['document']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], ] ]; From b3b99618603ba4aca2e6dc1b72ca4e1184e83416 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 13:22:26 +0530 Subject: [PATCH 010/173] init: move to module structure for database; start with columns. --- app/controllers/api/databases.php | 1956 +---------------- .../Modules/Databases/Http/Columns/Action.php | 417 ++++ .../Databases/Http/Columns/Boolean/Create.php | 84 + .../Databases/Http/Columns/Boolean/Update.php | 86 + .../Http/Columns/Datetime/Create.php | 106 + .../Http/Columns/Datetime/Update.php | 97 + .../Modules/Databases/Http/Columns/Delete.php | 164 ++ .../Databases/Http/Columns/Email/Create.php | 104 + .../Databases/Http/Columns/Email/Update.php | 98 + .../Databases/Http/Columns/Enum/Create.php | 113 + .../Databases/Http/Columns/Enum/Update.php | 102 + .../Databases/Http/Columns/Float/Create.php | 123 ++ .../Databases/Http/Columns/Float/Update.php | 109 + .../Modules/Databases/Http/Columns/Get.php | 112 + .../Databases/Http/Columns/IP/Create.php | 96 + .../Databases/Http/Columns/IP/Update.php | 98 + .../Databases/Http/Columns/Integer/Create.php | 123 ++ .../Databases/Http/Columns/Integer/Update.php | 109 + .../Http/Columns/Relationship/Create.php | 168 ++ .../Http/Columns/Relationship/Update.php | 103 + .../Databases/Http/Columns/String/Create.php | 122 + .../Databases/Http/Columns/String/Update.php | 101 + .../Databases/Http/Columns/URL/Create.php | 96 + .../Databases/Http/Columns/URL/Update.php | 98 + .../Modules/Databases/Http/Columns/XList.php | 125 ++ .../Platform/Modules/Databases/Module.php | 16 + .../Modules/Databases/Services/Http.php | 110 + .../Modules/Databases/Services/Workers.php | 15 + .../Databases}/Workers/Databases.php | 2 +- src/Appwrite/Platform/Services/Workers.php | 2 - 30 files changed, 3125 insertions(+), 1930 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Module.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Http.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Workers.php rename src/Appwrite/Platform/{ => Modules/Databases}/Workers/Databases.php (99%) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index d8556dd4fe..33b839fc41 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -6,13 +6,11 @@ use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\Email; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; -use Appwrite\Utopia\Database\Validator\Queries\Attributes; use Appwrite\Utopia\Database\Validator\Queries\Collections; use Appwrite\Utopia\Database\Validator\Queries\Databases; use Appwrite\Utopia\Database\Validator\Queries\Indexes; @@ -26,425 +24,32 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; -use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Exception\Index as IndexException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; -use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; -use Utopia\Database\Exception\Truncate as TruncateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Index as IndexValidator; -use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; -use Utopia\Database\Validator\Structure; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; -use Utopia\Validator\FloatValidator; -use Utopia\Validator\Integer; -use Utopia\Validator\IP; use Utopia\Validator\JSON; -use Utopia\Validator\Nullable; -use Utopia\Validator\Range; use Utopia\Validator\Text; -use Utopia\Validator\URL; use Utopia\Validator\WhiteList; -/** - * * Create column of varying type - * - * @param string $databaseId - * @param string $tableId - * @param Document $column - * @param Response $response - * @param Database $dbForProject - * @param EventDatabase $queueForDatabase - * @param Event $queueForEvents - * @return Document Newly created attribute document - * @throws AuthorizationException - * @throws Exception - * @throws LimitException - * @throws RestrictedException - * @throws StructureException - * @throws \Utopia\Database\Exception - * @throws ConflictException - * @throws Exception - */ -function createColumn(string $databaseId, string $tableId, Document $column, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document -{ - $key = $column->getAttribute('key'); - $type = $column->getAttribute('type', ''); - $size = $column->getAttribute('size', 0); - $required = $column->getAttribute('required', true); - $signed = $column->getAttribute('signed', true); // integers are signed by default - $array = $column->getAttribute('array', false); - $format = $column->getAttribute('format', ''); - $formatOptions = $column->getAttribute('formatOptions', []); - $filters = $column->getAttribute('filters', []); // filters are hidden from the endpoint - $default = $column->getAttribute('default'); - $options = $column->getAttribute('options', []); - - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - if (!empty($format)) { - if (!Structure::hasFormat($format, $type)) { - throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns."); - } - } - - // Must throw here since dbForProject->createAttribute is performed by db worker - if ($required && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); - } - - if ($array && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); - } - - if ($type === Database::VAR_RELATIONSHIP) { - $options['side'] = Database::RELATION_SIDE_PARENT; - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); - if ($relatedTable->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related table was not found.'); - } - } - - try { - $column = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), - 'key' => $key, - 'databaseInternalId' => $db->getInternalId(), - 'databaseId' => $db->getId(), - 'collectionInternalId' => $table->getInternalId(), - 'collectionId' => $tableId, - 'type' => $type, - 'status' => 'processing', // processing, available, failed, deleting, stuck - 'size' => $size, - 'required' => $required, - 'signed' => $signed, - 'default' => $default, - 'array' => $array, - 'format' => $format, - 'formatOptions' => $formatOptions, - 'filters' => $filters, - 'options' => $options, - ]); - - $dbForProject->checkAttribute($table, $column); - $column = $dbForProject->createDocument('attributes', $column); - } catch (DuplicateException) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); - } catch (LimitException) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); - } catch (\Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - throw $e; - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - - if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { - $twoWayKey = $options['twoWayKey']; - $options['relatedCollection'] = $table->getId(); - $options['twoWayKey'] = $key; - $options['side'] = Database::RELATION_SIDE_CHILD; - - try { - $twoWayAttribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey), - 'key' => $twoWayKey, - 'databaseInternalId' => $db->getInternalId(), - 'databaseId' => $db->getId(), - 'collectionInternalId' => $relatedTable->getInternalId(), - 'collectionId' => $relatedTable->getId(), - 'type' => $type, - 'status' => 'processing', // processing, available, failed, deleting, stuck - 'size' => $size, - 'required' => $required, - 'signed' => $signed, - 'default' => $default, - 'array' => $array, - 'format' => $format, - 'formatOptions' => $formatOptions, - 'filters' => $filters, - 'options' => $options, - ]); - - $dbForProject->checkAttribute($relatedTable, $twoWayAttribute); - $dbForProject->createDocument('attributes', $twoWayAttribute); - } catch (DuplicateException) { - $dbForProject->deleteDocument('attributes', $column->getId()); - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); - } catch (LimitException) { - $dbForProject->deleteDocument('attributes', $column->getId()); - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); - } catch (\Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); - throw $e; - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); - } - - $queueForDatabase - ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) - ->setDatabase($db) - ->setTable($table) - ->setRow($column); - - $queueForEvents - ->setContext('table', $table) - ->setContext('database', $db) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('columnId', $column->getId()); - - $response->setStatusCode(Response::STATUS_CODE_CREATED); - - return $column; -} - -function updateColumn( - string $databaseId, - string $tableId, - string $key, - Database $dbForProject, - Event $queueForEvents, - string $type, - int $size = null, - string $filter = null, - string|bool|int|float $default = null, - bool $required = null, - int|float|null $min = null, - int|float|null $max = null, - array $elements = null, - array $options = [], - string $newKey = null, -): Document { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - - if ($column->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } - - if ($column->getAttribute('status') !== 'available') { - throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE); - } - - if ($column->getAttribute(('type') !== $type)) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); - } - - if ($column->getAttribute('type') === Database::VAR_STRING && $column->getAttribute(('filter') !== $filter)) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); - } - - if ($required && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); - } - - if ($column->getAttribute('array', false) && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); - } - - $tableId = 'database_' . $db->getInternalId() . '_collection_' . $table->getInternalId(); - - $column - ->setAttribute('default', $default) - ->setAttribute('required', $required); - - if (!empty($size)) { - $column->setAttribute('size', $size); - } - - switch ($column->getAttribute('format')) { - case APP_DATABASE_ATTRIBUTE_INT_RANGE: - case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE: - $min ??= $column->getAttribute('formatOptions')['min']; - $max ??= $column->getAttribute('formatOptions')['max']; - - if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); - } - - if ($column->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { - $validator = new Range($min, $max, Database::VAR_INTEGER); - } else { - $validator = new Range($min, $max, Database::VAR_FLOAT); - - if (!is_null($default)) { - $default = \floatval($default); - } - } - - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); - } - - $options = [ - 'min' => $min, - 'max' => $max - ]; - $column->setAttribute('formatOptions', $options); - - break; - case APP_DATABASE_ATTRIBUTE_ENUM: - if (empty($elements)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Enum elements must not be empty'); - } - - foreach ($elements as $element) { - if (\strlen($element) === 0) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Each enum element must not be empty'); - } - } - - if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); - } - - $options = [ - 'elements' => $elements - ]; - - $column->setAttribute('formatOptions', $options); - - break; - } - - if ($type === Database::VAR_RELATIONSHIP) { - $primaryRowOptions = \array_merge($column->getAttribute('options', []), $options); - $column->setAttribute('options', $primaryRowOptions); - try { - $dbForProject->updateRelationship( - collection: $tableId, - id: $key, - newKey: $newKey, - onDelete: $primaryRowOptions['onDelete'], - ); - } catch (NotFoundException) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } - - if ($primaryRowOptions['twoWay']) { - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryRowOptions['relatedCollection']); - - $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey']); - - if (!empty($newKey) && $newKey !== $key) { - $options['twoWayKey'] = $newKey; - } - - $relatedOptions = \array_merge($relatedColumn->getAttribute('options'), $options); - $relatedColumn->setAttribute('options', $relatedOptions); - $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey'], $relatedColumn); - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - } - } else { - try { - $dbForProject->updateAttribute( - collection: $tableId, - id: $key, - size: $size, - required: $required, - default: $default, - formatOptions: $options, - newKey: $newKey ?? null - ); - } catch (TruncateException) { - throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE); - } catch (NotFoundException) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } catch (LimitException) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); - } catch (IndexException $e) { - throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); - } - } - - if (!empty($newKey) && $key !== $newKey) { - $originalUid = $column->getId(); - - $column - ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $newKey)) - ->setAttribute('key', $newKey); - - $dbForProject->updateDocument('attributes', $originalUid, $column); - - /** - * @var Document $index - */ - foreach ($table->getAttribute('indexes') as $index) { - /** - * @var string[] $columns - */ - $columns = $index->getAttribute('attributes', []); - $found = \array_search($key, $columns); - - if ($found !== false) { - $columns[$found] = $newKey; - $index->setAttribute('attributes', $columns); - $dbForProject->updateDocument('indexes', $index->getId(), $index); - } - } - } else { - $column = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key, $column); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $table->getId()); - - $queueForEvents - ->setContext('table', $table) - ->setContext('database', $db) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('columnId', $column->getId()); - - return $column; -} - App::init() ->groups(['api', 'database']) ->inject('request') @@ -1209,11 +814,11 @@ App::put('/v1/databases/:databaseId/tables/:tableId') 'database_' . $database->getInternalId(), $tableId, $table - ->setAttribute('name', $name) - ->setAttribute('$permissions', $permissions) - ->setAttribute('documentSecurity', $documentSecurity) - ->setAttribute('enabled', $enabled) - ->setAttribute('search', \implode(' ', [$tableId, $name])) + ->setAttribute('name', $name) + ->setAttribute('$permissions', $permissions) + ->setAttribute('documentSecurity', $documentSecurity) + ->setAttribute('enabled', $enabled) + ->setAttribute('search', \implode(' ', [$tableId, $name])) ); $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $permissions, $documentSecurity); @@ -1290,1509 +895,6 @@ App::delete('/v1/databases/:databaseId/tables/:tableId') $response->noContent(); }); -App::post('/v1/databases/:databaseId/tables/:tableId/columns/string') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/string') - ->desc('Create string column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createStringColumn', - description: '/docs/references/databases/create-string-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_STRING - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Text(0, 0), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - // Ensure attribute default is within required size - $validator = new Text($size, 0); - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); - } - - $filters = []; - - if ($encrypt) { - $filters[] = 'encrypt'; - } - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => $size, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_STRING); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/email') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/email') - ->desc('Create email column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createEmailColumn', - description: '/docs/references/databases/create-email-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_EMAIL, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Email(), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => 254, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_EMAIL); - }); - -App::post('/v1/databases/:databaseId/collections/:tableId/attributes/enum') - ->alias('/v1/database/collections/:tableId/attributes/enum') - ->desc('Create enum column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createEnumColumn', - description: '/docs/references/databases/create-attribute-enum.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_ENUM, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('elements', [], new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Text(0), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); - } - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_ENUM, - 'formatOptions' => ['elements' => $elements], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_ENUM); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/ip') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/ip') - ->desc('Create IP address column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createIpColumn', - description: '/docs/references/databases/create-ip-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_IP, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new IP(), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => 39, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_IP); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/url') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/url') - ->desc('Create URL column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createUrlColumn', - description: '/docs/references/databases/create-url-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_URL, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new URL(), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => 2000, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_URL); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/integer') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/integer') - ->desc('Create integer column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createIntegerColumn', - description: '/docs/references/databases/create-integer-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_INTEGER, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true) - ->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new Integer(), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - // Ensure attribute default is within range - $min ??= PHP_INT_MIN; - $max ??= PHP_INT_MAX; - - if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); - } - - $validator = new Range($min, $max, Database::VAR_INTEGER); - - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); - } - - $size = $max > 2147483647 ? 8 : 4; // Automatically create BigInt depending on max value - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_INTEGER, - 'size' => $size, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE, - 'formatOptions' => [ - 'min' => $min, - 'max' => $max, - ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $formatOptions = $column->getAttribute('formatOptions', []); - - if (!empty($formatOptions)) { - $column->setAttribute('min', \intval($formatOptions['min'])); - $column->setAttribute('max', \intval($formatOptions['max'])); - } - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_INTEGER); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/float') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/float') - ->desc('Create float column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createFloatColumn', - description: '/docs/references/databases/create-float-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_FLOAT, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true) - ->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new FloatValidator(), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - // Ensure attribute default is within range - $min ??= -PHP_FLOAT_MAX; - $max ??= PHP_FLOAT_MAX; - - if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); - } - - $validator = new Range($min, $max, Database::VAR_FLOAT); - - if (!\is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); - } - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_FLOAT, - 'required' => $required, - 'size' => 0, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, - 'formatOptions' => [ - 'min' => $min, - 'max' => $max, - ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $formatOptions = $column->getAttribute('formatOptions', []); - - if (!empty($formatOptions)) { - $column->setAttribute('min', \floatval($formatOptions['min'])); - $column->setAttribute('max', \floatval($formatOptions['max'])); - } - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_FLOAT); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/boolean') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean') - ->desc('Create boolean column') - ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createBooleanColumn', - description: '/docs/references/databases/create-boolean-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_BOOLEAN, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Boolean(), 'Default value for column when not provided. Cannot be set when column is required.', true) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_BOOLEAN, - 'size' => 0, - 'required' => $required, - 'default' => $default, - 'array' => $array, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_BOOLEAN); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/datetime') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime') - ->desc('Create datetime column') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createDatetimeColumn', - description: '/docs/references/databases/create-datetime-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_DATETIME, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) - ->param('array', false, new Boolean(), 'Is column an array?', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $filters[] = 'datetime'; - - $column = createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_DATETIME, - 'size' => 0, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_DATETIME); - }); - -App::post('/v1/databases/:databaseId/tables/:tableId/columns/relationship') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/relationship') - ->desc('Create relationship column') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'createRelationshipColumn', - description: '/docs/references/databases/create-relationship-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_ATTRIBUTE_RELATIONSHIP, - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('relatedTableId', '', new UID(), 'Related Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('type', '', new WhiteList([Database::RELATION_ONE_TO_ONE, Database::RELATION_MANY_TO_ONE, Database::RELATION_MANY_TO_MANY, Database::RELATION_ONE_TO_MANY], true), 'Relation type') - ->param('twoWay', false, new Boolean(), 'Is Two Way?', true) - ->param('key', null, new Key(), 'Column Key.', true) - ->param('twoWayKey', null, new Key(), 'Two Way Column Key.', true) - ->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function ( - string $databaseId, - string $tableId, - string $relatedTableId, - string $type, - bool $twoWay, - ?string $key, - ?string $twoWayKey, - string $onDelete, - Response $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ) { - $key ??= $relatedTableId; - $twoWayKey ??= $tableId; - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $relatedTableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId); - $relatedTable = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedTableDocument->getInternalId()); - - if ($relatedTable->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $columns = $table->getAttribute('attributes', []); - /** @var Document[] $columns */ - foreach ($columns as $column) { - if ($column->getAttribute('type') !== Database::VAR_RELATIONSHIP) { - continue; - } - - if (\strtolower($column->getId()) === \strtolower($key)) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); - } - - if ( - \strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && - $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() - ) { - // Console should provide a unique twoWayKey input! - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); - } - - if ( - $type === Database::RELATION_MANY_TO_MANY && - $column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && - $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() - ) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same table is currently not permitted.'); - } - } - - $column = createColumn( - $databaseId, - $tableId, - new Document([ - 'key' => $key, - 'type' => Database::VAR_RELATIONSHIP, - 'size' => 0, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - 'options' => [ - 'relatedCollection' => $relatedTableId, - 'relationType' => $type, - 'twoWay' => $twoWay, - 'twoWayKey' => $twoWayKey, - 'onDelete' => $onDelete, - ] - ]), - $response, - $dbForProject, - $queueForDatabase, - $queueForEvents - ); - - $options = $column->getAttribute('options', []); - - foreach ($options as $key => $option) { - $column->setAttribute($key, $option); - } - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($column, Response::MODEL_ATTRIBUTE_RELATIONSHIP); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/columns') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes') - ->desc('List columns') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'listColumns', - description: '/docs/references/databases/list-attributes.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_LIST - ) - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject) { - /** @var Document $database */ - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $queries = Query::parseQueries($queries); - - \array_push( - $queries, - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$table->getInternalId()]), - ); - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - - $cursor = \reset($cursor); - - if ($cursor) { - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $columnId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->find('attributes', [ - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$table->getInternalId()]), - Query::equal('key', [$columnId]), - Query::limit(1), - ])); - - if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Column '{$columnId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument[0]); - } - - $filters = Query::groupByType($queries)['filters']; - try { - $columns = $dbForProject->find('attributes', $queries); - $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); - } - - $response->dynamic(new Document([ - 'attributes' => $columns, - 'total' => $total, - ]), Response::MODEL_ATTRIBUTE_LIST); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/columns/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') - ->desc('Get column') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'getColumn', - description: '/docs/references/databases/get-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: [ - Response::MODEL_ATTRIBUTE_BOOLEAN, - Response::MODEL_ATTRIBUTE_INTEGER, - Response::MODEL_ATTRIBUTE_FLOAT, - Response::MODEL_ATTRIBUTE_EMAIL, - Response::MODEL_ATTRIBUTE_ENUM, - Response::MODEL_ATTRIBUTE_URL, - Response::MODEL_ATTRIBUTE_IP, - Response::MODEL_ATTRIBUTE_DATETIME, - Response::MODEL_ATTRIBUTE_RELATIONSHIP, - Response::MODEL_ATTRIBUTE_STRING - ] - ), - ] - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - - if ($column->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } - - // Select response model based on type and format - $type = $column->getAttribute('type'); - $format = $column->getAttribute('format'); - $options = $column->getAttribute('options', []); - - foreach ($options as $key => $option) { - $column->setAttribute($key, $option); - } - - $model = match ($type) { - Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, - Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, - Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, - Database::VAR_DATETIME => Response::MODEL_ATTRIBUTE_DATETIME, - Database::VAR_RELATIONSHIP => Response::MODEL_ATTRIBUTE_RELATIONSHIP, - Database::VAR_STRING => match ($format) { - APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, - APP_DATABASE_ATTRIBUTE_ENUM => Response::MODEL_ATTRIBUTE_ENUM, - APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, - APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, - default => Response::MODEL_ATTRIBUTE_STRING, - }, - default => Response::MODEL_ATTRIBUTE, - }; - - $response->dynamic($column, $model); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/string/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/string/:key') - ->desc('Update string column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateStringColumn', - description: '/docs/references/databases/update-string-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_STRING, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Maximum size of the string attribute.', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - size: $size, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_STRING); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/email/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/email/:key') - ->desc('Update email column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateEmailColumn', - description: '/docs/references/databases/update-email-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_EMAIL, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Nullable(new Email()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_EMAIL, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_EMAIL); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/enum/:key') - ->desc('Update enum column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateEnumColumn', - description: '/docs/references/databases/update-enum-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_ENUM, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('elements', null, new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Nullable(new Text(0)), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_ENUM, - default: $default, - required: $required, - elements: $elements, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_ENUM); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/ip/:key') - ->desc('Update IP address column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateIpColumn', - description: '/docs/references/databases/update-ip-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_IP, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Nullable(new IP()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_IP, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_IP); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/url/:key') - ->desc('Update URL column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateUrlColumn', - description: '/docs/references/databases/update-url-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_URL, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Nullable(new URL()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_URL, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_URL); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/integer/:key') - ->desc('Update integer column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateIntegerColumn', - description: '/docs/references/databases/update-integer-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_INTEGER, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true) - ->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new Nullable(new Integer()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_INTEGER, - default: $default, - required: $required, - min: $min, - max: $max, - newKey: $newKey - ); - - $formatOptions = $column->getAttribute('formatOptions', []); - - if (!empty($formatOptions)) { - $column->setAttribute('min', \intval($formatOptions['min'])); - $column->setAttribute('max', \intval($formatOptions['max'])); - } - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_INTEGER); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/float/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/float/:key') - ->desc('Update float column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateFloatColumn', - description: '/docs/references/databases/update-float-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_FLOAT, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true) - ->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true) - ->param('default', null, new Nullable(new FloatValidator()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_FLOAT, - default: $default, - required: $required, - min: $min, - max: $max, - newKey: $newKey - ); - - $formatOptions = $column->getAttribute('formatOptions', []); - - if (!empty($formatOptions)) { - $column->setAttribute('min', \floatval($formatOptions['min'])); - $column->setAttribute('max', \floatval($formatOptions['max'])); - } - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_FLOAT); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean/:key') - ->desc('Update boolean column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateBooleanColumn', - description: '/docs/references/databases/update-boolean-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_BOOLEAN, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_BOOLEAN, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_BOOLEAN); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime/:key') - ->desc('Update dateTime column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateDatetimeColumn', - description: '/docs/references/databases/update-datetime-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_DATETIME, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for column when not provided. Cannot be set when column is required.', injections: ['dbForProject']) - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - $column = updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_DATETIME, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_DATETIME); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/:key/relationship') - ->desc('Update relationship column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'updateRelationshipColumn', - description: '/docs/references/databases/update-relationship-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_ATTRIBUTE_RELATIONSHIP, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->param('onDelete', null, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function ( - string $databaseId, - string $tableId, - string $key, - ?string $onDelete, - ?string $newKey, - Response $response, - Database $dbForProject, - Event $queueForEvents - ) { - $column = updateColumn( - $databaseId, - $tableId, - $key, - $dbForProject, - $queueForEvents, - type: Database::VAR_RELATIONSHIP, - required: false, - options: [ - 'onDelete' => $onDelete - ], - newKey: $newKey - ); - - $options = $column->getAttribute('options', []); - - foreach ($options as $key => $option) { - $column->setAttribute($key, $option); - } - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic($column, Response::MODEL_ATTRIBUTE_RELATIONSHIP); - }); - -App::delete('/v1/databases/:databaseId/tables/:tableId/columns/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') - ->desc('Delete column') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') - ->label('audits.event', 'column.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'columns', - name: 'deleteColumn', - description: '/docs/references/databases/delete-attribute.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Column Key.') - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - - if ($column->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } - - /** - * Check index dependency - */ - $validator = new IndexDependencyValidator( - $table->getAttribute('indexes'), - $dbForProject->getAdapter()->getSupportForCastIndexArray(), - ); - - if (! $validator->isValid($column)) { - throw new Exception(Exception::INDEX_DEPENDENCY); - } - - // Only update status if removing available attribute - if ($column->getAttribute('status') === 'available') { - $column = $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'deleting')); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - - if ($column->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $column->getAttribute('options'); - if ($options['twoWay']) { - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - - if ($relatedTable->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); - - if ($relatedColumn->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } - - if ($relatedColumn->getAttribute('status') === 'available') { - $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'deleting')); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); - } - } - - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setTable($table) - ->setDatabase($db) - ->setRow($column); - - // Select response model based on type and format - $type = $column->getAttribute('type'); - $format = $column->getAttribute('format'); - - $model = match ($type) { - Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, - Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, - Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, - Database::VAR_DATETIME => Response::MODEL_ATTRIBUTE_DATETIME, - Database::VAR_RELATIONSHIP => Response::MODEL_ATTRIBUTE_RELATIONSHIP, - Database::VAR_STRING => match ($format) { - APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, - APP_DATABASE_ATTRIBUTE_ENUM => Response::MODEL_ATTRIBUTE_ENUM, - APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, - APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, - default => Response::MODEL_ATTRIBUTE_STRING, - }, - default => Response::MODEL_ATTRIBUTE, - }; - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('columnId', $column->getId()) - ->setContext('table', $table) - ->setContext('database', $db) - ->setPayload($response->output($column, $model)); - - $response->noContent(); - }); - App::post('/v1/databases/:databaseId/tables/:tableId/indexes') ->alias('/v1/databases/:databaseId/collections/:tableId/indexes') ->desc('Create index') @@ -4276,7 +2378,7 @@ App::get('/v1/databases/usage') Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); @@ -4305,7 +2407,7 @@ App::get('/v1/databases/usage') }; foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['total'] = $stats[$metric]['total']; $usage[$metric]['data'] = []; $leap = time() - ($days['limit'] * $days['factor']); while ($leap < time()) { @@ -4319,16 +2421,16 @@ App::get('/v1/databases/usage') } $response->dynamic(new Document([ 'range' => $range, - 'databasesTotal' => $usage[$metrics[0]]['total'], + 'databasesTotal' => $usage[$metrics[0]]['total'], 'collectionsTotal' => $usage[$metrics[1]]['total'], - 'documentsTotal' => $usage[$metrics[2]]['total'], - 'storageTotal' => $usage[$metrics[3]]['total'], + 'documentsTotal' => $usage[$metrics[2]]['total'], + 'storageTotal' => $usage[$metrics[3]]['total'], 'databasesReadsTotal' => $usage[$metrics[4]]['total'], 'databasesWritesTotal' => $usage[$metrics[5]]['total'], - 'databases' => $usage[$metrics[0]]['data'], + 'databases' => $usage[$metrics[0]]['data'], 'collections' => $usage[$metrics[1]]['data'], - 'documents' => $usage[$metrics[2]]['data'], - 'storage' => $usage[$metrics[3]]['data'], + 'documents' => $usage[$metrics[2]]['data'], + 'storage' => $usage[$metrics[3]]['data'], 'databasesReads' => $usage[$metrics[4]]['data'], 'databasesWrites' => $usage[$metrics[5]]['data'], ]), Response::MODEL_USAGE_DATABASES); @@ -4359,7 +2461,7 @@ App::get('/v1/databases/:databaseId/usage') ->inject('dbForProject') ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) { - $database = $dbForProject->getDocument('databases', $databaseId); + $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -4378,7 +2480,7 @@ App::get('/v1/databases/:databaseId/usage') Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); @@ -4407,7 +2509,7 @@ App::get('/v1/databases/:databaseId/usage') }; foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['total'] = $stats[$metric]['total']; $usage[$metric]['data'] = []; $leap = time() - ($days['limit'] * $days['factor']); while ($leap < time()) { @@ -4422,16 +2524,16 @@ App::get('/v1/databases/:databaseId/usage') $response->dynamic(new Document([ 'range' => $range, - 'collectionsTotal' => $usage[$metrics[0]]['total'], - 'documentsTotal' => $usage[$metrics[1]]['total'], - 'storageTotal' => $usage[$metrics[2]]['total'], + 'collectionsTotal' => $usage[$metrics[0]]['total'], + 'documentsTotal' => $usage[$metrics[1]]['total'], + 'storageTotal' => $usage[$metrics[2]]['total'], 'databaseReadsTotal' => $usage[$metrics[3]]['total'], 'databaseWritesTotal' => $usage[$metrics[4]]['total'], - 'collections' => $usage[$metrics[0]]['data'], - 'documents' => $usage[$metrics[1]]['data'], - 'storage' => $usage[$metrics[2]]['data'], - 'databaseReads' => $usage[$metrics[3]]['data'], - 'databaseWrites' => $usage[$metrics[4]]['data'], + 'collections' => $usage[$metrics[0]]['data'], + 'documents' => $usage[$metrics[1]]['data'], + 'storage' => $usage[$metrics[2]]['data'], + 'databaseReads' => $usage[$metrics[3]]['data'], + 'databaseWrites' => $usage[$metrics[4]]['data'], ]), Response::MODEL_USAGE_DATABASE); }); @@ -4479,7 +2581,7 @@ App::get('/v1/databases/:databaseId/tables/:tableId/usage') Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); @@ -4496,7 +2598,7 @@ App::get('/v1/databases/:databaseId/tables/:tableId/usage') $stats[$metric]['data'] = []; foreach ($results as $result) { $stats[$metric]['data'][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), + 'value' => $result->getAttribute('value'), ]; } } @@ -4508,7 +2610,7 @@ App::get('/v1/databases/:databaseId/tables/:tableId/usage') }; foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['total'] = $stats[$metric]['total']; $usage[$metric]['data'] = []; $leap = time() - ($days['limit'] * $days['factor']); while ($leap < time()) { @@ -4523,7 +2625,7 @@ App::get('/v1/databases/:databaseId/tables/:tableId/usage') $response->dynamic(new Document([ 'range' => $range, - 'documentsTotal' => $usage[$metrics[0]]['total'], - 'documents' => $usage[$metrics[0]]['data'], + 'documentsTotal' => $usage[$metrics[0]]['total'], + 'documents' => $usage[$metrics[0]]['data'], ]), Response::MODEL_USAGE_COLLECTION); }); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php new file mode 100644 index 0000000000..70c26594a3 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php @@ -0,0 +1,417 @@ +getAttribute('key'); + $type = $column->getAttribute('type', ''); + $size = $column->getAttribute('size', 0); + $required = $column->getAttribute('required', true); + $signed = $column->getAttribute('signed', true); // integers are signed by default + $array = $column->getAttribute('array', false); + $format = $column->getAttribute('format', ''); + $formatOptions = $column->getAttribute('formatOptions', []); + $filters = $column->getAttribute('filters', []); // filters are hidden from the endpoint + $default = $column->getAttribute('default'); + $options = $column->getAttribute('options', []); + + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + if (!empty($format)) { + if (!Structure::hasFormat($format, $type)) { + throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns."); + } + } + + // Must throw here since dbForProject->createAttribute is performed by db worker + if ($required && isset($default)) { + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); + } + + if ($array && isset($default)) { + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); + } + + if ($type === Database::VAR_RELATIONSHIP) { + $options['side'] = Database::RELATION_SIDE_PARENT; + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); + if ($relatedTable->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related table was not found.'); + } + } + + try { + $column = new Document([ + '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), + 'key' => $key, + 'databaseInternalId' => $db->getInternalId(), + 'databaseId' => $db->getId(), + 'collectionInternalId' => $table->getInternalId(), + 'collectionId' => $tableId, + 'type' => $type, + 'status' => 'processing', // processing, available, failed, deleting, stuck + 'size' => $size, + 'required' => $required, + 'signed' => $signed, + 'default' => $default, + 'array' => $array, + 'format' => $format, + 'formatOptions' => $formatOptions, + 'filters' => $filters, + 'options' => $options, + ]); + + $dbForProject->checkAttribute($table, $column); + $column = $dbForProject->createDocument('attributes', $column); + } catch (DuplicateException) { + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); + } catch (LimitException) { + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + } catch (Throwable $e) { + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); + throw $e; + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); + + if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { + $twoWayKey = $options['twoWayKey']; + $options['relatedCollection'] = $table->getId(); + $options['twoWayKey'] = $key; + $options['side'] = Database::RELATION_SIDE_CHILD; + + try { + $twoWayAttribute = new Document([ + '$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey), + 'key' => $twoWayKey, + 'databaseInternalId' => $db->getInternalId(), + 'databaseId' => $db->getId(), + 'collectionInternalId' => $relatedTable->getInternalId(), + 'collectionId' => $relatedTable->getId(), + 'type' => $type, + 'status' => 'processing', // processing, available, failed, deleting, stuck + 'size' => $size, + 'required' => $required, + 'signed' => $signed, + 'default' => $default, + 'array' => $array, + 'format' => $format, + 'formatOptions' => $formatOptions, + 'filters' => $filters, + 'options' => $options, + ]); + + $dbForProject->checkAttribute($relatedTable, $twoWayAttribute); + $dbForProject->createDocument('attributes', $twoWayAttribute); + } catch (DuplicateException) { + $dbForProject->deleteDocument('attributes', $column->getId()); + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); + } catch (LimitException) { + $dbForProject->deleteDocument('attributes', $column->getId()); + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + } catch (Throwable $e) { + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + throw $e; + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + } + + $queueForDatabase + ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) + ->setDatabase($db) + ->setTable($table) + ->setRow($column); + + $queueForEvents + ->setContext('table', $table) + ->setContext('database', $db) + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('columnId', $column->getId()); + + $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); + + return $column; + } + + protected function updateColumn( + string $databaseId, + string $tableId, + string $key, + Database $dbForProject, + Event $queueForEvents, + string $type, + int $size = null, + string $filter = null, + string|bool|int|float $default = null, + bool $required = null, + int|float|null $min = null, + int|float|null $max = null, + array $elements = null, + array $options = [], + string $newKey = null, + ): Document { + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); + + if ($column->isEmpty()) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } + + if ($column->getAttribute('status') !== 'available') { + throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE); + } + + if ($column->getAttribute(('type') !== $type)) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); + } + + if ($column->getAttribute('type') === Database::VAR_STRING && $column->getAttribute(('filter') !== $filter)) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); + } + + if ($required && isset($default)) { + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); + } + + if ($column->getAttribute('array', false) && isset($default)) { + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); + } + + $tableId = 'database_' . $db->getInternalId() . '_collection_' . $table->getInternalId(); + + $column + ->setAttribute('default', $default) + ->setAttribute('required', $required); + + if (!empty($size)) { + $column->setAttribute('size', $size); + } + + switch ($column->getAttribute('format')) { + case APP_DATABASE_ATTRIBUTE_INT_RANGE: + case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE: + $min ??= $column->getAttribute('formatOptions')['min']; + $max ??= $column->getAttribute('formatOptions')['max']; + + if ($min > $max) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); + } + + if ($column->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { + $validator = new Range($min, $max, Database::VAR_INTEGER); + } else { + $validator = new Range($min, $max, Database::VAR_FLOAT); + + if (!is_null($default)) { + $default = \floatval($default); + } + } + + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + } + + $options = [ + 'min' => $min, + 'max' => $max + ]; + $column->setAttribute('formatOptions', $options); + + break; + case APP_DATABASE_ATTRIBUTE_ENUM: + if (empty($elements)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Enum elements must not be empty'); + } + + foreach ($elements as $element) { + if (\strlen($element) === 0) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Each enum element must not be empty'); + } + } + + if (!is_null($default) && !in_array($default, $elements)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); + } + + $options = [ + 'elements' => $elements + ]; + + $column->setAttribute('formatOptions', $options); + + break; + } + + if ($type === Database::VAR_RELATIONSHIP) { + $primaryRowOptions = \array_merge($column->getAttribute('options', []), $options); + $column->setAttribute('options', $primaryRowOptions); + try { + $dbForProject->updateRelationship( + collection: $tableId, + id: $key, + newKey: $newKey, + onDelete: $primaryRowOptions['onDelete'], + ); + } catch (NotFoundException) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } + + if ($primaryRowOptions['twoWay']) { + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryRowOptions['relatedCollection']); + + $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey']); + + if (!empty($newKey) && $newKey !== $key) { + $options['twoWayKey'] = $newKey; + } + + $relatedOptions = \array_merge($relatedColumn->getAttribute('options'), $options); + $relatedColumn->setAttribute('options', $relatedOptions); + $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey'], $relatedColumn); + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + } + } else { + try { + $dbForProject->updateAttribute( + collection: $tableId, + id: $key, + size: $size, + required: $required, + default: $default, + formatOptions: $options, + newKey: $newKey ?? null + ); + } catch (TruncateException) { + throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE); + } catch (NotFoundException) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } catch (LimitException) { + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + } catch (IndexException $e) { + throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); + } + } + + if (!empty($newKey) && $key !== $newKey) { + $originalUid = $column->getId(); + + $column + ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $newKey)) + ->setAttribute('key', $newKey); + + $dbForProject->updateDocument('attributes', $originalUid, $column); + + /** + * @var Document $index + */ + foreach ($table->getAttribute('indexes') as $index) { + /** + * @var string[] $columns + */ + $columns = $index->getAttribute('attributes', []); + $found = \array_search($key, $columns); + + if ($found !== false) { + $columns[$found] = $newKey; + $index->setAttribute('attributes', $columns); + $dbForProject->updateDocument('indexes', $index->getId(), $index); + } + } + } else { + $column = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key, $column); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $table->getId()); + + $queueForEvents + ->setContext('table', $table) + ->setContext('database', $db) + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('columnId', $column->getId()); + + return $column; + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php new file mode 100644 index 0000000000..dab0527fa7 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php @@ -0,0 +1,84 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean') + ->desc('Create boolean column') + ->groups(['api', 'database', 'schema']) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createBooleanColumn', + description: '/docs/references/databases/create-boolean-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Boolean(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { + + $column = $this->createColumn($databaseId, $tableId, new Document([ + 'key' => $key, + 'type' => Database::VAR_BOOLEAN, + 'size' => 0, + 'required' => $required, + 'default' => $default, + 'array' => $array, + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php new file mode 100644 index 0000000000..35148fc174 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php @@ -0,0 +1,86 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean/:key') + ->desc('Update boolean column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateBooleanColumn', + description: '/docs/references/databases/update-boolean-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_BOOLEAN, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php new file mode 100644 index 0000000000..36ddfb4dfa --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php @@ -0,0 +1,106 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime') + ->desc('Create datetime column') + ->groups(['api', 'database']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createDatetimeColumn', + description: '/docs/references/databases/create-datetime-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $filters = ['datetime']; + + $column = $this->createColumn( + $databaseId, + $tableId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_DATETIME, + 'size' => 0, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'filters' => $filters, + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php new file mode 100644 index 0000000000..d323ed4fd0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php @@ -0,0 +1,97 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime/:key') + ->desc('Update dateTime column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateDatetimeColumn', + description: '/docs/references/databases/update-datetime-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for column when not provided. Cannot be set when column is required.', injections: ['dbForProject']) + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_DATETIME, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php new file mode 100644 index 0000000000..9baa857fec --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php @@ -0,0 +1,164 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') + ->desc('Delete column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'deleteColumn', + description: '/docs/references/databases/delete-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents, + ): void { + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); + if ($column->isEmpty()) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } + + $validator = new IndexDependencyValidator( + $table->getAttribute('indexes'), + $dbForProject->getAdapter()->getSupportForCastIndexArray(), + ); + if (!$validator->isValid($column)) { + throw new Exception(Exception::INDEX_DEPENDENCY); + } + + if ($column->getAttribute('status') === 'available') { + $column = $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'deleting')); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); + + if ($column->getAttribute('type') === Database::VAR_RELATIONSHIP) { + $options = $column->getAttribute('options'); + if ($options['twoWay']) { + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); + if ($relatedTable->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); + if ($relatedColumn->isEmpty()) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } + + if ($relatedColumn->getAttribute('status') === 'available') { + $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'deleting')); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + } + } + + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) + ->setTable($table) + ->setDatabase($db) + ->setRow($column); + + $type = $column->getAttribute('type'); + $format = $column->getAttribute('format'); + + $model = match ($type) { + Database::VAR_BOOLEAN => UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + Database::VAR_INTEGER => UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + Database::VAR_FLOAT => UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + Database::VAR_DATETIME => UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, + Database::VAR_STRING => match ($format) { + APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_ATTRIBUTE_IP, + APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_ATTRIBUTE_URL, + default => UtopiaResponse::MODEL_ATTRIBUTE_STRING, + }, + default => UtopiaResponse::MODEL_ATTRIBUTE, + }; + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('columnId', $column->getId()) + ->setContext('table', $table) + ->setContext('database', $db) + ->setPayload($response->output($column, $model)); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php new file mode 100644 index 0000000000..1d51e26efc --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php @@ -0,0 +1,104 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/email') + ->desc('Create email column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createEmailColumn', + description: '/docs/references/databases/create-email-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Email(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + bool $array, + \Appwrite\Utopia\Response $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $column = $this->createColumn( + $databaseId, + $tableId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => 254, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php new file mode 100644 index 0000000000..53e03c93a2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php @@ -0,0 +1,98 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/email/:key') + ->desc('Update email column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateEmailColumn', + description: '/docs/references/databases/update-email-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Email()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_EMAIL, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php new file mode 100644 index 0000000000..95ed0b0630 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php @@ -0,0 +1,113 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/enum') + ->desc('Create enum column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createEnumColumn', + description: '/docs/references/databases/create-attribute-enum.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('elements', [], new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of enum values.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Text(0), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + array $elements, + ?bool $required, + ?string $default, + bool $array, + \Appwrite\Utopia\Response $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + if (!is_null($default) && !in_array($default, $elements, true)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); + } + + $column = $this->createColumn( + $databaseId, + $tableId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_ENUM, + 'formatOptions' => ['elements' => $elements], + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_ENUM); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php new file mode 100644 index 0000000000..d52dd161e4 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php @@ -0,0 +1,102 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/enum/:key') + ->desc('Update enum column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateEnumColumn', + description: '/docs/references/databases/update-enum-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('elements', null, new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Updated list of enum values.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Text(0)), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?array $elements, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_ENUM, + default: $default, + required: $required, + elements: $elements, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_ENUM); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php new file mode 100644 index 0000000000..0782f8eb29 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -0,0 +1,123 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/float') + ->desc('Create float column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createFloatColumn', + description: '/docs/references/databases/create-float-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('min', null, new FloatValidator(), 'Minimum value', true) + ->param('max', null, new FloatValidator(), 'Maximum value', true) + ->param('default', null, new FloatValidator(), 'Default value. Cannot be set when required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?float $min, + ?float $max, + ?float $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $min ??= -INF; + $max ??= INF; + + if ($min > $max) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum must be less than or equal to maximum'); + } + + $validator = new Range($min, $max, Database::VAR_FLOAT); + if (!\is_null($default) && !$validator->isValid($default)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + } + + $column = $this->createColumn($databaseId, $tableId, new Document([ + 'key' => $key, + 'type' => Database::VAR_FLOAT, + 'size' => 0, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, + 'formatOptions' => ['min' => $min, 'max' => $max], + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $formatOptions = $column->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $column->setAttribute('min', \floatval($formatOptions['min'])); + $column->setAttribute('max', \floatval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php new file mode 100644 index 0000000000..c585205388 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php @@ -0,0 +1,109 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/float/:key') + ->desc('Update float column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateFloatColumn', + description: '/docs/references/databases/update-float-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('min', null, new FloatValidator(), 'Minimum value', true) + ->param('max', null, new FloatValidator(), 'Maximum value', true) + ->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?float $min, + ?float $max, + ?float $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_FLOAT, + default: $default, + required: $required, + min: $min, + max: $max, + newKey: $newKey + ); + + $formatOptions = $column->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $column->setAttribute('min', \floatval($formatOptions['min'])); + $column->setAttribute('max', \floatval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php new file mode 100644 index 0000000000..14744f0442 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php @@ -0,0 +1,112 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') + ->desc('Get column') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'getColumn', + description: '/docs/references/databases/get-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: [ + UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + UtopiaResponse::MODEL_ATTRIBUTE_URL, + UtopiaResponse::MODEL_ATTRIBUTE_IP, + UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, + UtopiaResponse::MODEL_ATTRIBUTE_STRING, + ] + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key); + if ($column->isEmpty()) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } + + $type = $column->getAttribute('type'); + $format = $column->getAttribute('format'); + $options = $column->getAttribute('options', []); + + foreach ($options as $optKey => $optValue) { + $column->setAttribute($optKey, $optValue); + } + + $model = match ($type) { + Database::VAR_BOOLEAN => UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + Database::VAR_INTEGER => UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + Database::VAR_FLOAT => UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + Database::VAR_DATETIME => UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, + Database::VAR_STRING => match ($format) { + APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_ATTRIBUTE_IP, + APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_ATTRIBUTE_URL, + default => UtopiaResponse::MODEL_ATTRIBUTE_STRING, + }, + default => UtopiaResponse::MODEL_ATTRIBUTE, + }; + + $response->dynamic($column, $model); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php new file mode 100644 index 0000000000..b3916a5fad --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php @@ -0,0 +1,96 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/ip') + ->desc('Create IP address column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createIpColumn', + description: '/docs/references/databases/create-ip-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_IP, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new IP(), 'Default value. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + bool $array, + \Appwrite\Utopia\Response $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $column = $this->createColumn($databaseId, $tableId, new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => 39, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_IP, + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_IP); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php new file mode 100644 index 0000000000..6da08a2c9f --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php @@ -0,0 +1,98 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/ip/:key') + ->desc('Update IP address column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateIpColumn', + description: '/docs/references/databases/update-ip-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_IP, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_IP, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_IP); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php new file mode 100644 index 0000000000..7a26d10bc8 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php @@ -0,0 +1,123 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/integer') + ->desc('Create integer column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createIntegerColumn', + description: '/docs/references/databases/create-integer-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('min', null, new Integer(), 'Minimum value', true) + ->param('max', null, new Integer(), 'Maximum value', true) + ->param('default', null, new Integer(), 'Default value. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?int $min, + ?int $max, + ?int $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $min ??= \PHP_INT_MIN; + $max ??= \PHP_INT_MAX; + + if ($min > $max) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); + } + + $validator = new Range($min, $max, Database::VAR_INTEGER); + if (!\is_null($default) && !$validator->isValid($default)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + } + + $size = $max > 2147483647 ? 8 : 4; + + $column = $this->createColumn($databaseId, $tableId, new Document([ + 'key' => $key, + 'type' => Database::VAR_INTEGER, + 'size' => $size, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE, + 'formatOptions' => ['min' => $min, 'max' => $max], + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $formatOptions = $column->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $column->setAttribute('min', \intval($formatOptions['min'])); + $column->setAttribute('max', \intval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php new file mode 100644 index 0000000000..27616c07c9 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php @@ -0,0 +1,109 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/integer/:key') + ->desc('Update integer column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateIntegerColumn', + description: '/docs/references/databases/update-integer-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('min', null, new Integer(), 'Minimum value', true) + ->param('max', null, new Integer(), 'Maximum value', true) + ->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?int $min, + ?int $max, + ?int $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_INTEGER, + default: $default, + required: $required, + min: $min, + max: $max, + newKey: $newKey + ); + + $formatOptions = $column->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $column->setAttribute('min', \intval($formatOptions['min'])); + $column->setAttribute('max', \intval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php new file mode 100644 index 0000000000..32cb235b62 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -0,0 +1,168 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/relationship') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/relationship') + ->desc('Create relationship column') + ->groups(['api', 'database']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createRelationshipColumn', + description: '/docs/references/databases/create-relationship-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('relatedTableId', '', new UID(), 'Related Table ID.') + ->param('type', '', new WhiteList([ + Database::RELATION_ONE_TO_ONE, + Database::RELATION_MANY_TO_ONE, + Database::RELATION_MANY_TO_MANY, + Database::RELATION_ONE_TO_MANY + ], true), 'Relation type') + ->param('twoWay', false, new Boolean(), 'Is Two Way?', true) + ->param('key', null, new Key(), 'Column Key.', true) + ->param('twoWayKey', null, new Key(), 'Two Way Column Key.', true) + ->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([ + Database::RELATION_MUTATE_CASCADE, + Database::RELATION_MUTATE_RESTRICT, + Database::RELATION_MUTATE_SET_NULL + ], true), 'Constraints option', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $relatedTableId, + string $type, + bool $twoWay, + ?string $key, + ?string $twoWayKey, + string $onDelete, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $key ??= $relatedTableId; + $twoWayKey ??= $tableId; + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $relatedTableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId); + $relatedTable = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedTableDocument->getInternalId()); + if ($relatedTable->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $columns = $table->getAttribute('attributes', []); + foreach ($columns as $column) { + if ($column->getAttribute('type') !== Database::VAR_RELATIONSHIP) { + continue; + } + + if (\strtolower($column->getId()) === \strtolower($key)) { + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); + } + + if ( + \strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && + $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() + ) { + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique.'); + } + + if ( + $type === Database::RELATION_MANY_TO_MANY && + $column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && + $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() + ) { + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Only one "manyToMany" relationship per table is allowed.'); + } + } + + $column = $this->createColumn($databaseId, $tableId, new Document([ + 'key' => $key, + 'type' => Database::VAR_RELATIONSHIP, + 'size' => 0, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + 'options' => [ + 'relatedCollection' => $relatedTableId, + 'relationType' => $type, + 'twoWay' => $twoWay, + 'twoWayKey' => $twoWayKey, + 'onDelete' => $onDelete, + ] + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + foreach ($column->getAttribute('options', []) as $k => $option) { + $column->setAttribute($k, $option); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php new file mode 100644 index 0000000000..34231ad611 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php @@ -0,0 +1,103 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key/relationship') + ->desc('Update relationship column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateRelationshipColumn', + description: '/docs/references/databases/update-relationship-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('onDelete', null, new WhiteList([ + Database::RELATION_MUTATE_CASCADE, + Database::RELATION_MUTATE_RESTRICT, + Database::RELATION_MUTATE_SET_NULL + ], true), 'Constraints option', true) + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?string $onDelete, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + $databaseId, + $tableId, + $key, + $dbForProject, + $queueForEvents, + type: Database::VAR_RELATIONSHIP, + required: false, + options: [ + 'onDelete' => $onDelete + ], + newKey: $newKey + ); + + foreach ($column->getAttribute('options', []) as $k => $option) { + $column->setAttribute($k, $option); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php new file mode 100644 index 0000000000..1987f5fdbe --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php @@ -0,0 +1,122 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/string') + ->desc('Create string column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createStringColumn', + description: '/docs/references/databases/create-string-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_STRING + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Text(0, 0), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?int $size, + ?bool $required, + ?string $default, + bool $array, + bool $encrypt, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + // Ensure default fits in the given size + $validator = new Text($size, 0); + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + } + + $filters = []; + if ($encrypt) { + $filters[] = 'encrypt'; + } + + $column = $this->createColumn( + $databaseId, + $tableId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => $size, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'filters' => $filters, + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_STRING); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php new file mode 100644 index 0000000000..a73ae0f9b1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php @@ -0,0 +1,101 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/string/:key') + ->desc('Update string column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateStringColumn', + description: '/docs/references/databases/update-string-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_STRING, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string attribute.', true) + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + ?int $size, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + databaseId: $databaseId, + tableId: $tableId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + size: $size, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_STRING); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php new file mode 100644 index 0000000000..74eedfa98b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php @@ -0,0 +1,96 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url') + ->desc('Create URL column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') + ->label('audits.event', 'column.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'createUrlColumn', + description: '/docs/references/databases/create-url-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_ATTRIBUTE_URL, + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new URL(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('array', false, new Boolean(), 'Is column an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $column = $this->createColumn($databaseId, $tableId, new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => 2000, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_URL, + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_URL); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php new file mode 100644 index 0000000000..109b9c575b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php @@ -0,0 +1,98 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url/:key') + ->desc('Update URL column') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') + ->label('audits.event', 'column.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'updateUrlColumn', + description: '/docs/references/databases/update-url-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_URL, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->param('required', null, new Boolean(), 'Is column required?') + ->param('default', null, new Nullable(new URL()), 'Default value for column when not provided. Cannot be set when column is required.') + ->param('newKey', null, new Key(), 'New Column Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $column = $this->updateColumn( + $databaseId, + $tableId, + $key, + $dbForProject, + $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_URL, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_URL); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php new file mode 100644 index 0000000000..f2343e927b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -0,0 +1,125 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes') + ->desc('List columns') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'listColumns', + description: '/docs/references/databases/list-attributes.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_ATTRIBUTE_LIST + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $queries = Query::parseQueries($queries); + + \array_push( + $queries, + Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]) + ); + + $cursor = \array_filter( + $queries, + fn ($query) => \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]) + ); + $cursor = \reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $columnId = $cursor->getValue(); + $cursorDocument = Authorization::skip( + fn () => $dbForProject->find('attributes', [ + Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('key', [$columnId]), + Query::limit(1), + ]) + ); + + if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Column '{$columnId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument[0]); + } + + $filters = Query::groupByType($queries)['filters']; + + try { + $columns = $dbForProject->find('attributes', $queries); + $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); + } catch (OrderException $e) { + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); + } + + $response->dynamic(new Document([ + 'attributes' => $columns, + 'total' => $total, + ]), UtopiaResponse::MODEL_ATTRIBUTE_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Module.php b/src/Appwrite/Platform/Modules/Databases/Module.php new file mode 100644 index 0000000000..f8686999dd --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Module.php @@ -0,0 +1,16 @@ +addService('http', new Http()); + $this->addService('workers', new Workers()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php new file mode 100644 index 0000000000..1cd482d61a --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -0,0 +1,110 @@ +type = Service::TYPE_HTTP; + + $this->registerDatabaseActions(); + $this->registerTableActions(); + $this->registerColumnActions(); + $this->registerIndexActions(); + $this->registerRowActions(); + } + + private function registerDatabaseActions() + { + + } + + private function registerTableActions() + { + + } + + private function registerColumnActions(): void + { + // Column top level actions + $this->addAction(GetColumn::getName(), new GetColumn()); + $this->addAction(DeleteColumn::getName(), new DeleteColumn()); + $this->addAction(ListColumns::getName(), new ListColumns()); + + // Column: Boolean + $this->addAction(CreateBoolean::getName(), new CreateBoolean()); + $this->addAction(UpdateBoolean::getName(), new UpdateBoolean()); + + // Column: Datetime + $this->addAction(CreateDatetime::getName(), new CreateDatetime()); + $this->addAction(UpdateDatetime::getName(), new UpdateDatetime()); + + // Column: Email + $this->addAction(CreateEmail::getName(), new CreateEmail()); + $this->addAction(UpdateEmail::getName(), new UpdateEmail()); + + // Column: Enum + $this->addAction(CreateEnum::getName(), new CreateEnum()); + $this->addAction(UpdateEnum::getName(), new UpdateEnum()); + + // Column: Float + $this->addAction(CreateFloat::getName(), new CreateFloat()); + $this->addAction(UpdateFloat::getName(), new UpdateFloat()); + + // Column: Integer + $this->addAction(CreateInteger::getName(), new CreateInteger()); + $this->addAction(UpdateInteger::getName(), new UpdateInteger()); + + // Column: IP + $this->addAction(CreateIP::getName(), new CreateIP()); + $this->addAction(UpdateIP::getName(), new UpdateIP()); + + // Column: Relationship + $this->addAction(CreateRelationship::getName(), new CreateRelationship()); + $this->addAction(UpdateRelationship::getName(), new UpdateRelationship()); + + // Column: String + $this->addAction(CreateString::getName(), new CreateString()); + $this->addAction(UpdateString::getName(), new UpdateString()); + + // Column: URL + $this->addAction(CreateURL::getName(), new CreateURL()); + $this->addAction(UpdateURL::getName(), new UpdateURL()); + } + + private function registerIndexActions() + { + + } + + private function registerRowActions() + { + + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Workers.php b/src/Appwrite/Platform/Modules/Databases/Services/Workers.php new file mode 100644 index 0000000000..55388ea7ff --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Workers.php @@ -0,0 +1,15 @@ +type = Service::TYPE_WORKER; + $this->addAction(Databases::getName(), new Databases()); + } +} diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php similarity index 99% rename from src/Appwrite/Platform/Workers/Databases.php rename to src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 1f3b76d6eb..6564239630 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -1,6 +1,6 @@ addAction(Audits::getName(), new Audits()) ->addAction(Certificates::getName(), new Certificates()) - ->addAction(Databases::getName(), new Databases()) ->addAction(Deletes::getName(), new Deletes()) ->addAction(Functions::getName(), new Functions()) ->addAction(Mails::getName(), new Mails()) From 9c1ec6fe029497803854509b3bf4351c74ad2893 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 13:37:25 +0530 Subject: [PATCH 011/173] update: move the init hook. --- app/controllers/api/databases.php | 12 ------------ app/controllers/general.php | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 33b839fc41..6b19c96be6 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -50,18 +50,6 @@ use Utopia\Validator\JSON; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -App::init() - ->groups(['api', 'database']) - ->inject('request') - ->inject('dbForProject') - ->action(function (Request $request, Database $dbForProject) { - $timeout = \intval($request->getHeader('x-appwrite-timeout')); - - if (!empty($timeout) && App::isDevelopment()) { - $dbForProject->setTimeout($timeout); - } - }); - App::post('/v1/databases') ->desc('Create database') ->groups(['api', 'database']) diff --git a/app/controllers/general.php b/app/controllers/general.php index fdef1e9cee..32323d6e05 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1576,6 +1576,20 @@ foreach (Config::getParam('services', []) as $service) { } } +// legacy controller init hooks as the endpoint controller logic is now moved to a module structure. +// 1. databases +App::init() + ->groups(['api', 'database']) + ->inject('request') + ->inject('dbForProject') + ->action(function (Request $request, Database $dbForProject) { + $timeout = \intval($request->getHeader('x-appwrite-timeout')); + + if (!empty($timeout) && App::isDevelopment()) { + $dbForProject->setTimeout($timeout); + } + }); + // Check for any errors found while we were initialising the SDK Methods. if (!empty(Method::getErrors())) { throw new \Exception('Errors found during SDK initialization:' . PHP_EOL . implode(PHP_EOL, Method::getErrors())); From 1945a64083bd9fc261ad3fb3e1f006180a927ff2 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 14:08:10 +0530 Subject: [PATCH 012/173] update: move DB ops to module. --- app/controllers/api/databases.php | 389 ------------------ .../Databases/Http/Databases/Create.php | 123 ++++++ .../Databases/Http/Databases/Delete.php | 88 ++++ .../Modules/Databases/Http/Databases/Get.php | 65 +++ .../Databases/Http/Databases/Logs/XList.php | 143 +++++++ .../Databases/Http/Databases/Update.php | 81 ++++ .../Databases/Http/Databases/XList.php | 107 +++++ .../Modules/Databases/Services/Http.php | 21 +- 8 files changed, 623 insertions(+), 394 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 6b19c96be6..dd181f8894 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -12,9 +12,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Collections; -use Appwrite\Utopia\Database\Validator\Queries\Databases; use Appwrite\Utopia\Database\Validator\Queries\Indexes; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; use Utopia\App; @@ -50,393 +48,6 @@ use Utopia\Validator\JSON; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -App::post('/v1/databases') - ->desc('Create database') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].create') - ->label('scope', 'databases.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'database.create') - ->label('audits.resource', 'database/{response.$id}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'databases', - name: 'create', - description: '/docs/references/databases/create.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_DATABASE, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('name', '', new Text(128), 'Database name. Max length: 128 chars.') - ->param('enabled', true, new Boolean(), 'Is the database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) { - - $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; - - try { - $dbForProject->createDocument('databases', new Document([ - '$id' => $databaseId, - 'name' => $name, - 'enabled' => $enabled, - 'search' => implode(' ', [$databaseId, $name]), - ])); - $database = $dbForProject->getDocument('databases', $databaseId); - - $collections = (Config::getParam('collections', [])['databases'] ?? [])['collections'] ?? []; - if (empty($collections)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.'); - } - - $attributes = []; - $indexes = []; - - foreach ($collections['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => $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'] ?? '' - ]); - } - - foreach ($collections['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => $index['$id'], - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - $dbForProject->createCollection('database_' . $database->getInternalId(), $attributes, $indexes); - } catch (DuplicateException) { - throw new Exception(Exception::DATABASE_ALREADY_EXISTS); - } - - $queueForEvents->setParam('databaseId', $database->getId()); - - $response - ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($database, Response::MODEL_DATABASE); - }); - -App::get('/v1/databases') - ->desc('List databases') - ->groups(['api', 'database']) - ->label('scope', 'databases.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'databases', - name: 'list', - description: '/docs/references/databases/list.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DATABASE_LIST, - ) - ], - contentType: ContentType::JSON - )) - ->param('queries', [], new Databases(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Databases::ALLOWED_ATTRIBUTES), true) - ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) - ->inject('response') - ->inject('dbForProject') - ->action(function (array $queries, string $search, Response $response, Database $dbForProject) { - $queries = Query::parseQueries($queries); - - if (!empty($search)) { - $queries[] = Query::search('search', $search); - } - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - $cursor = reset($cursor); - if ($cursor) { - /** @var Query $cursor */ - - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $databaseId = $cursor->getValue(); - $cursorDocument = $dbForProject->getDocument('databases', $databaseId); - - if ($cursorDocument->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Database '{$databaseId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument); - } - - $filterQueries = Query::groupByType($queries)['filters']; - - try { - $databases = $dbForProject->find('databases', $queries); - $total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); - } - $response->dynamic(new Document([ - 'databases' => $databases, - 'total' => $total, - ]), Response::MODEL_DATABASE_LIST); - }); - -App::get('/v1/databases/:databaseId') - ->desc('Get database') - ->groups(['api', 'database']) - ->label('scope', 'databases.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'databases', - name: 'get', - description: '/docs/references/databases/get.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DATABASE, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, Response $response, Database $dbForProject) { - - $database = $dbForProject->getDocument('databases', $databaseId); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $response->dynamic($database, Response::MODEL_DATABASE); - }); - -App::get('/v1/databases/:databaseId/logs') - ->desc('List database logs') - ->groups(['api', 'database']) - ->label('scope', 'databases.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'logs', - name: 'listLogs', - description: '/docs/references/databases/get-logs.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_LOG_LIST, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) - ->inject('response') - ->inject('dbForProject') - ->inject('locale') - ->inject('geodb') - ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - - $database = $dbForProject->getDocument('databases', $databaseId); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - // Temp fix for logs - $queries[] = Query::or([ - Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), - Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), - ]); - - $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId; - $logs = $audit->getLogsByResource($resource, $queries); - - $output = []; - - foreach ($logs as $i => &$log) { - $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - - $detector = new Detector($log['userAgent']); - $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - - $os = $detector->getOS(); - $client = $detector->getClient(); - $device = $detector->getDevice(); - - $output[$i] = new Document([ - 'event' => $log['event'], - 'userId' => ID::custom($log['data']['userId']), - 'userEmail' => $log['data']['userEmail'] ?? null, - 'userName' => $log['data']['userName'] ?? null, - 'mode' => $log['data']['mode'] ?? null, - 'ip' => $log['ip'], - 'time' => $log['time'], - 'osCode' => $os['osCode'], - 'osName' => $os['osName'], - 'osVersion' => $os['osVersion'], - 'clientType' => $client['clientType'], - 'clientCode' => $client['clientCode'], - 'clientName' => $client['clientName'], - 'clientVersion' => $client['clientVersion'], - 'clientEngine' => $client['clientEngine'], - 'clientEngineVersion' => $client['clientEngineVersion'], - 'deviceName' => $device['deviceName'], - 'deviceBrand' => $device['deviceBrand'], - 'deviceModel' => $device['deviceModel'] - ]); - - $record = $geodb->get($log['ip']); - - if ($record) { - $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; - $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); - } else { - $output[$i]['countryCode'] = '--'; - $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); - } - } - - $response->dynamic(new Document([ - 'total' => $audit->countLogsByResource($resource, $queries), - 'logs' => $output, - ]), Response::MODEL_LOG_LIST); - }); - - -App::put('/v1/databases/:databaseId') - ->desc('Update database') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'databases.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].update') - ->label('audits.event', 'database.update') - ->label('audits.resource', 'database/{response.$id}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'databases', - name: 'update', - description: '/docs/references/databases/update.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DATABASE, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('name', null, new Text(128), 'Database name. Max length: 128 chars.') - ->param('enabled', true, new Boolean(), 'Is database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) { - - $database = $dbForProject->getDocument('databases', $databaseId); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $database = $dbForProject->updateDocument('databases', $databaseId, $database - ->setAttribute('name', $name) - ->setAttribute('enabled', $enabled) - ->setAttribute('search', implode(' ', [$databaseId, $name]))); - - $queueForEvents->setParam('databaseId', $database->getId()); - - $response->dynamic($database, Response::MODEL_DATABASE); - }); - -App::delete('/v1/databases/:databaseId') - ->desc('Delete database') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'databases.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].delete') - ->label('audits.event', 'database.delete') - ->label('audits.resource', 'database/{request.databaseId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'databases', - name: 'delete', - description: '/docs/references/databases/delete.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - - $database = $dbForProject->getDocument('databases', $databaseId); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - if (!$dbForProject->deleteDocument('databases', $databaseId)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); - } - - $dbForProject->purgeCachedDocument('databases', $database->getId()); - $dbForProject->purgeCachedCollection('databases_' . $database->getInternalId()); - - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_DATABASE) - ->setDatabase($database); - - $queueForEvents - ->setParam('databaseId', $database->getId()) - ->setPayload($response->output($database, Response::MODEL_DATABASE)); - - $response->noContent(); - }); - App::post('/v1/databases/:databaseId/tables') ->alias('/v1/databases/:databaseId/collections') ->desc('Create table') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php new file mode 100644 index 0000000000..a734cbdd22 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php @@ -0,0 +1,123 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases') + ->desc('Create database') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].create') + ->label('scope', 'databases.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'database.create') + ->label('audits.resource', 'database/{response.$id}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'databases', + name: 'create', + description: '/docs/references/databases/create.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: UtopiaResponse::MODEL_DATABASE, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('name', '', new Text(128), 'Database name. Max length: 128 chars.') + ->param('enabled', true, new Boolean(), 'Is the database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $name, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; + + try { + $dbForProject->createDocument('databases', new Document([ + '$id' => $databaseId, + 'name' => $name, + 'enabled' => $enabled, + 'search' => implode(' ', [$databaseId, $name]), + ])); + $database = $dbForProject->getDocument('databases', $databaseId); + + $collections = (Config::getParam('collections', [])['databases'] ?? [])['collections'] ?? []; + if (empty($collections)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.'); + } + + $attributes = []; + $indexes = []; + + foreach ($collections['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => $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'] ?? '' + ]); + } + + foreach ($collections['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => $index['$id'], + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + $dbForProject->createCollection('database_' . $database->getInternalId(), $attributes, $indexes); + } catch (DuplicateException) { + throw new Exception(Exception::DATABASE_ALREADY_EXISTS); + } + + $queueForEvents->setParam('databaseId', $database->getId()); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($database, UtopiaResponse::MODEL_DATABASE); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php new file mode 100644 index 0000000000..e0651b5e5b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php @@ -0,0 +1,88 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId') + ->desc('Delete database') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'databases.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].delete') + ->label('audits.event', 'database.delete') + ->label('audits.resource', 'database/{request.databaseId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'databases', + name: 'delete', + description: '/docs/references/databases/delete.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + if (!$dbForProject->deleteDocument('databases', $databaseId)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); + } + + $dbForProject->purgeCachedDocument('databases', $database->getId()); + $dbForProject->purgeCachedCollection('databases_' . $database->getInternalId()); + + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_DATABASE) + ->setDatabase($database); + + $queueForEvents + ->setParam('databaseId', $database->getId()) + ->setPayload($response->output($database, UtopiaResponse::MODEL_DATABASE)); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php new file mode 100644 index 0000000000..67b61bceb2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php @@ -0,0 +1,65 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId') + ->desc('Get database') + ->groups(['api', 'database']) + ->label('scope', 'databases.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'databases', + name: 'get', + description: '/docs/references/databases/get.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_DATABASE, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $response->dynamic($database, UtopiaResponse::MODEL_DATABASE); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php new file mode 100644 index 0000000000..56358aa723 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php @@ -0,0 +1,143 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/logs') + ->desc('List database logs') + ->groups(['api', 'database']) + ->label('scope', 'databases.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'logs', + name: 'listLogs', + description: '/docs/references/databases/get-logs.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_LOG_LIST, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) + ->inject('response') + ->inject('dbForProject') + ->inject('locale') + ->inject('geodb') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + // Temp fix for logs + $queries[] = Query::or([ + Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), + Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), + ]); + + $audit = new Audit($dbForProject); + $resource = 'database/' . $databaseId; + $logs = $audit->getLogsByResource($resource, $queries); + + $output = []; + + foreach ($logs as $i => &$log) { + $log['userAgent'] = $log['userAgent'] ?: 'UNKNOWN'; + $detector = new Detector($log['userAgent']); + $detector->skipBotDetection(); + + $os = $detector->getOS(); + $client = $detector->getClient(); + $device = $detector->getDevice(); + + $output[$i] = new Document([ + 'event' => $log['event'], + 'userId' => ID::custom($log['data']['userId']), + 'userEmail' => $log['data']['userEmail'] ?? null, + 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'ip' => $log['ip'], + 'time' => $log['time'], + 'osCode' => $os['osCode'], + 'osName' => $os['osName'], + 'osVersion' => $os['osVersion'], + 'clientType' => $client['clientType'], + 'clientCode' => $client['clientCode'], + 'clientName' => $client['clientName'], + 'clientVersion' => $client['clientVersion'], + 'clientEngine' => $client['clientEngine'], + 'clientEngineVersion' => $client['clientEngineVersion'], + 'deviceName' => $device['deviceName'], + 'deviceBrand' => $device['deviceBrand'], + 'deviceModel' => $device['deviceModel'], + ]); + + $record = $geodb->get($log['ip']); + if ($record) { + $countryCode = strtolower($record['country']['iso_code']); + $output[$i]['countryCode'] = $locale->getText("countries.{$countryCode}", false) ? $countryCode : '--'; + $output[$i]['countryName'] = $locale->getText("countries.{$countryCode}", $locale->getText('locale.country.unknown')); + } else { + $output[$i]['countryCode'] = '--'; + $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); + } + } + + $response->dynamic(new Document([ + 'total' => $audit->countLogsByResource($resource, $queries), + 'logs' => $output, + ]), UtopiaResponse::MODEL_LOG_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php new file mode 100644 index 0000000000..49c92a65a2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php @@ -0,0 +1,81 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId') + ->desc('Update database') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'databases.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].update') + ->label('audits.event', 'database.update') + ->label('audits.resource', 'database/{response.$id}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'databases', + name: 'update', + description: '/docs/references/databases/update.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_DATABASE, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('name', null, new Text(128), 'Database name. Max length: 128 chars.') + ->param('enabled', true, new Boolean(), 'Is database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $name, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $database = $dbForProject->updateDocument('databases', $databaseId, $database + ->setAttribute('name', $name) + ->setAttribute('enabled', $enabled) + ->setAttribute('search', implode(' ', [$databaseId, $name]))); + + $queueForEvents->setParam('databaseId', $database->getId()); + + $response->dynamic($database, UtopiaResponse::MODEL_DATABASE); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php new file mode 100644 index 0000000000..7453fb39f4 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php @@ -0,0 +1,107 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases') + ->desc('List databases') + ->groups(['api', 'database']) + ->label('scope', 'databases.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'databases', + name: 'list', + description: '/docs/references/databases/list.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_DATABASE_LIST, + ) + ], + contentType: ContentType::JSON + )) + ->param('queries', [], new Databases(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Databases::ALLOWED_ATTRIBUTES), true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void + { + $queries = Query::parseQueries($queries); + + if (!empty($search)) { + $queries[] = Query::search('search', $search); + } + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + $cursor = reset($cursor); + if ($cursor) { + /** @var Query $cursor */ + + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $databaseId = $cursor->getValue(); + $cursorDocument = $dbForProject->getDocument('databases', $databaseId); + + if ($cursorDocument->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Database '{$databaseId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + + $filterQueries = Query::groupByType($queries)['filters']; + + try { + $databases = $dbForProject->find('databases', $queries); + $total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT); + } catch (OrderException $e) { + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + } + $response->dynamic(new Document([ + 'databases' => $databases, + 'total' => $total, + ]), UtopiaResponse::MODEL_DATABASE_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index 1cd482d61a..de0de71e05 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -25,6 +25,12 @@ use Appwrite\Platform\Modules\Databases\Http\Columns\String\Update as UpdateStri use Appwrite\Platform\Modules\Databases\Http\Columns\URL\Create as CreateURL; use Appwrite\Platform\Modules\Databases\Http\Columns\URL\Update as UpdateURL; use Appwrite\Platform\Modules\Databases\Http\Columns\XList as ListColumns; +use Appwrite\Platform\Modules\Databases\Http\Databases\Create as CreateDatabase; +use Appwrite\Platform\Modules\Databases\Http\Databases\Delete as DeleteDatabase; +use Appwrite\Platform\Modules\Databases\Http\Databases\Get as GetDatabase; +use Appwrite\Platform\Modules\Databases\Http\Databases\Logs\XList as ListDatabaseLogs; +use Appwrite\Platform\Modules\Databases\Http\Databases\Update as UpdateDatabase; +use Appwrite\Platform\Modules\Databases\Http\Databases\XList as ListDatabases; use Utopia\Platform\Service; class Http extends Service @@ -40,12 +46,17 @@ class Http extends Service $this->registerRowActions(); } - private function registerDatabaseActions() + private function registerDatabaseActions(): void { - + $this->addAction(CreateDatabase::getName(), new CreateDatabase()); + $this->addAction(GetDatabase::getName(), new GetDatabase()); + $this->addAction(UpdateDatabase::getName(), new UpdateDatabase()); + $this->addAction(DeleteDatabase::getName(), new DeleteDatabase()); + $this->addAction(ListDatabases::getName(), new ListDatabases()); + $this->addAction(ListDatabaseLogs::getName(), new ListDatabaseLogs()); } - private function registerTableActions() + private function registerTableActions(): void { } @@ -98,12 +109,12 @@ class Http extends Service $this->addAction(UpdateURL::getName(), new UpdateURL()); } - private function registerIndexActions() + private function registerIndexActions(): void { } - private function registerRowActions() + private function registerRowActions(): void { } From 8efc1543e9c0889621cdf2ae5011358b464ef8d0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 17:05:14 +0530 Subject: [PATCH 013/173] update: move Table ops to module. --- app/controllers/api/databases.php | 448 ------------------ .../Modules/Databases/Http/Tables/Create.php | 116 +++++ .../Modules/Databases/Http/Tables/Delete.php | 95 ++++ .../Modules/Databases/Http/Tables/Get.php | 74 +++ .../Databases/Http/Tables/Logs/XList.php | 153 ++++++ .../Modules/Databases/Http/Tables/Update.php | 110 +++++ .../Modules/Databases/Http/Tables/XList.php | 117 +++++ 7 files changed, 665 insertions(+), 448 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index dd181f8894..bc80502521 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -11,7 +11,6 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; -use Appwrite\Utopia\Database\Validator\Queries\Collections; use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; @@ -23,7 +22,6 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; @@ -48,452 +46,6 @@ use Utopia\Validator\JSON; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -App::post('/v1/databases/:databaseId/tables') - ->alias('/v1/databases/:databaseId/collections') - ->desc('Create table') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'table.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'createTable', - description: '/docs/references/databases/create-collection.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_COLLECTION, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('mode') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $tableId = $tableId == 'unique()' ? ID::unique() : $tableId; - - // Map aggregate permissions into the multiple permissions they represent. - $permissions = Permission::aggregate($permissions) ?? []; - - try { - $table = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ - '$id' => $tableId, - 'databaseInternalId' => $database->getInternalId(), - 'databaseId' => $databaseId, - '$permissions' => $permissions, - 'documentSecurity' => $documentSecurity, - 'enabled' => $enabled, - 'name' => $name, - 'search' => implode(' ', [$tableId, $name]), - ])); - - $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); - } catch (DuplicateException) { - throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); - } catch (LimitException) { - throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); - } - - $queueForEvents - ->setContext('database', $database) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()); - - $response - ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($table, Response::MODEL_COLLECTION); - }); - -App::get('/v1/databases/:databaseId/tables') - ->alias('/v1/databases/:databaseId/collections') - ->desc('List tables') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'listTables', - description: '/docs/references/databases/list-collections.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_COLLECTION_LIST, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('queries', [], new Collections(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Collections::ALLOWED_ATTRIBUTES), true) - ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('mode') - ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $queries = Query::parseQueries($queries); - - if (!empty($search)) { - $queries[] = Query::search('search', $search); - } - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - $cursor = reset($cursor); - if ($cursor) { - /** @var Query $cursor */ - - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $tableId = $cursor->getValue(); - $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($cursorDocument->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Table '{$tableId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument); - } - - $filterQueries = Query::groupByType($queries)['filters']; - - try { - $tables = $dbForProject->find('database_' . $database->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); - } - - // TODO: collections > tables - $response->dynamic(new Document([ - 'collections' => $tables, - 'total' => $total, - ]), Response::MODEL_COLLECTION_LIST); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId') - ->alias('/v1/databases/:databaseId/collections/:tableId') - ->desc('Get table') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'getTable', - description: '/docs/references/databases/get-collection.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_COLLECTION, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID.') - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $tableId, Response $response, Database $dbForProject) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $response->dynamic($table, Response::MODEL_COLLECTION); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/logs') - ->alias('/v1/databases/:databaseId/collections/:tableId/logs') - ->desc('List table logs') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'listTableLogs', - description: '/docs/references/databases/get-collection-logs.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_LOG_LIST, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID.') - ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) - ->inject('response') - ->inject('dbForProject') - ->inject('locale') - ->inject('geodb') - ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - // Temp fix for logs - $queries[] = Query::or([ - Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), - Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), - ]); - - $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/table/' . $tableId; - $logs = $audit->getLogsByResource($resource, $queries); - - $output = []; - - foreach ($logs as $i => &$log) { - $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - - $detector = new Detector($log['userAgent']); - $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - - $os = $detector->getOS(); - $client = $detector->getClient(); - $device = $detector->getDevice(); - - $output[$i] = new Document([ - 'event' => $log['event'], - 'userId' => $log['data']['userId'], - 'userEmail' => $log['data']['userEmail'] ?? null, - 'userName' => $log['data']['userName'] ?? null, - 'mode' => $log['data']['mode'] ?? null, - 'ip' => $log['ip'], - 'time' => $log['time'], - 'osCode' => $os['osCode'], - 'osName' => $os['osName'], - 'osVersion' => $os['osVersion'], - 'clientType' => $client['clientType'], - 'clientCode' => $client['clientCode'], - 'clientName' => $client['clientName'], - 'clientVersion' => $client['clientVersion'], - 'clientEngine' => $client['clientEngine'], - 'clientEngineVersion' => $client['clientEngineVersion'], - 'deviceName' => $device['deviceName'], - 'deviceBrand' => $device['deviceBrand'], - 'deviceModel' => $device['deviceModel'] - ]); - - $record = $geodb->get($log['ip']); - - if ($record) { - $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; - $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); - } else { - $output[$i]['countryCode'] = '--'; - $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); - } - } - - $response->dynamic(new Document([ - 'total' => $audit->countLogsByResource($resource, $queries), - 'logs' => $output, - ]), Response::MODEL_LOG_LIST); - }); - - -App::put('/v1/databases/:databaseId/tables/:tableId') - ->alias('/v1/databases/:databaseId/collections/:tableId') - ->desc('Update table') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].update') - ->label('audits.event', 'table.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'updateTable', - description: '/docs/references/databases/update-collection.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_COLLECTION, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID.') - ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('mode') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $permissions ??= $table->getPermissions() ?? []; - - // Map aggregate permissions into the multiple permissions they represent. - $permissions = Permission::aggregate($permissions); - - $enabled ??= $table->getAttribute('enabled', true); - - $table = $dbForProject->updateDocument( - 'database_' . $database->getInternalId(), - $tableId, - $table - ->setAttribute('name', $name) - ->setAttribute('$permissions', $permissions) - ->setAttribute('documentSecurity', $documentSecurity) - ->setAttribute('enabled', $enabled) - ->setAttribute('search', \implode(' ', [$tableId, $name])) - ); - - $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $permissions, $documentSecurity); - - $queueForEvents - ->setContext('database', $database) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()); - - $response->dynamic($table, Response::MODEL_COLLECTION); - }); - -App::delete('/v1/databases/:databaseId/tables/:tableId') - ->alias('/v1/databases/:databaseId/collections/:tableId') - ->desc('Delete table') - ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].delete') - ->label('audits.event', 'table.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'deleteTable', - description: '/docs/references/databases/delete-collection.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID.') - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->inject('mode') - ->action(function (string $databaseId, string $tableId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $tableId)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); - } - - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); - - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_COLLECTION) - ->setDatabase($database) - ->setTable($table); - - $queueForEvents - ->setContext('database', $database) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setPayload($response->output($table, Response::MODEL_COLLECTION)); - - $response->noContent(); - }); - App::post('/v1/databases/:databaseId/tables/:tableId/indexes') ->alias('/v1/databases/:databaseId/collections/:tableId/indexes') ->desc('Create index') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php new file mode 100644 index 0000000000..ead5a64f88 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php @@ -0,0 +1,116 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables') + ->httpAlias('/v1/databases/:databaseId/collections') + ->desc('Create table') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].create') + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'table.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'createTable', + description: '/docs/references/databases/create-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: UtopiaResponse::MODEL_COLLECTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $tableId = $tableId === 'unique()' ? ID::unique() : $tableId; + + $permissions = Permission::aggregate($permissions) ?? []; + + try { + $table = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ + '$id' => $tableId, + 'databaseInternalId' => $database->getInternalId(), + 'databaseId' => $databaseId, + '$permissions' => $permissions, + 'documentSecurity' => $documentSecurity, + 'enabled' => $enabled, + 'name' => $name, + 'search' => \implode(' ', [$tableId, $name]), + ])); + + $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); + } catch (DuplicateException) { + throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); + } catch (LimitException) { + throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); + } + + $queueForEvents + ->setContext('database', $database) + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($table, UtopiaResponse::MODEL_COLLECTION); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php new file mode 100644 index 0000000000..b3120ef36d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -0,0 +1,95 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId') + ->desc('Delete table') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].delete') + ->label('audits.event', 'table.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'deleteTable', + description: '/docs/references/databases/delete-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$thiss, 'action']); + } + + public function action(string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { + $database = \Utopia\Database\Validator\Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $tableId)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); + } + + $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); + + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_COLLECTION) + ->setDatabase($database) + ->setTable($table); + + $queueForEvents + ->setContext('database', $database) + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setPayload($response->output($table, UtopiaResponse::MODEL_COLLECTION)); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php new file mode 100644 index 0000000000..2b0f77fc81 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php @@ -0,0 +1,74 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId') + ->desc('Get table') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'getTable', + description: '/docs/references/databases/get-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_COLLECTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $response->dynamic($table, UtopiaResponse::MODEL_COLLECTION); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php new file mode 100644 index 0000000000..44a850424f --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php @@ -0,0 +1,153 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/logs') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/logs') + ->desc('List table logs') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'listTableLogs', + description: '/docs/references/databases/get-collection-logs.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_LOG_LIST, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) + ->inject('response') + ->inject('dbForProject') + ->inject('locale') + ->inject('geodb') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + // Temp fix for logs + $queries[] = Query::or([ + Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), + Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), + ]); + + $audit = new Audit($dbForProject); + $resource = 'database/' . $databaseId . '/table/' . $tableId; + $logs = $audit->getLogsByResource($resource, $queries); + + $output = []; + + foreach ($logs as $i => &$log) { + $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; + + $detector = new Detector($log['userAgent']); + $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) + + $os = $detector->getOS(); + $client = $detector->getClient(); + $device = $detector->getDevice(); + + $output[$i] = new Document([ + 'event' => $log['event'], + 'userId' => $log['data']['userId'], + 'userEmail' => $log['data']['userEmail'] ?? null, + 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'ip' => $log['ip'], + 'time' => $log['time'], + 'osCode' => $os['osCode'], + 'osName' => $os['osName'], + 'osVersion' => $os['osVersion'], + 'clientType' => $client['clientType'], + 'clientCode' => $client['clientCode'], + 'clientName' => $client['clientName'], + 'clientVersion' => $client['clientVersion'], + 'clientEngine' => $client['clientEngine'], + 'clientEngineVersion' => $client['clientEngineVersion'], + 'deviceName' => $device['deviceName'], + 'deviceBrand' => $device['deviceBrand'], + 'deviceModel' => $device['deviceModel'] + ]); + + $record = $geodb->get($log['ip']); + + if ($record) { + $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; + $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); + } else { + $output[$i]['countryCode'] = '--'; + $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); + } + } + + $response->dynamic(new Document([ + 'total' => $audit->countLogsByResource($resource, $queries), + 'logs' => $output, + ]), UtopiaResponse::MODEL_LOG_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php new file mode 100644 index 0000000000..2db6c5d328 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -0,0 +1,110 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId') + ->desc('Update table') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].update') + ->label('audits.event', 'table.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'updateTable', + description: '/docs/references/databases/update-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_COLLECTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $permissions ??= $table->getPermissions() ?? []; + + // Map aggregate permissions into the multiple permissions they represent. + $permissions = Permission::aggregate($permissions); + + $enabled ??= $table->getAttribute('enabled', true); + + $table = $dbForProject->updateDocument( + 'database_' . $database->getInternalId(), + $tableId, + $table + ->setAttribute('name', $name) + ->setAttribute('$permissions', $permissions) + ->setAttribute('documentSecurity', $documentSecurity) + ->setAttribute('enabled', $enabled) + ->setAttribute('search', \implode(' ', [$tableId, $name])) + ); + + $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $permissions, $documentSecurity); + + $queueForEvents + ->setContext('database', $database) + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()); + + $response->dynamic($table, UtopiaResponse::MODEL_COLLECTION); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php new file mode 100644 index 0000000000..d75150f2bb --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -0,0 +1,117 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables') + ->httpAlias('/v1/databases/:databaseId/collections') + ->desc('List tables') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'listTables', + description: '/docs/references/databases/list-collections.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_COLLECTION_LIST, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('queries', [], new Collections(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Collections::ALLOWED_ATTRIBUTES), true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $queries = Query::parseQueries($queries); + + if (!empty($search)) { + $queries[] = Query::search('search', $search); + } + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + $cursor = reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $tableId = $cursor->getValue(); + $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + + if ($cursorDocument->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Table '{$tableId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + + $filterQueries = Query::groupByType($queries)['filters']; + + try { + $tables = $dbForProject->find('database_' . $database->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); + } catch (OrderException $e) { + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + } + + $response->dynamic(new Document([ + 'collections' => $tables, // TODO: consider renaming to 'tables' + 'total' => $total, + ]), UtopiaResponse::MODEL_COLLECTION_LIST); + } +} From 3271ef57695f56b2a3e0dfa4d229167dad7eb576 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 17:16:37 +0530 Subject: [PATCH 014/173] update: move Index ops to module. --- app/controllers/api/databases.php | 389 ------------------ .../Modules/Databases/Http/Indexes/Create.php | 217 ++++++++++ .../Modules/Databases/Http/Indexes/Delete.php | 109 +++++ .../Modules/Databases/Http/Indexes/Get.php | 80 ++++ .../Modules/Databases/Http/Indexes/XList.php | 129 ++++++ 5 files changed, 535 insertions(+), 389 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index bc80502521..63a4e3fbb7 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2,7 +2,6 @@ use Appwrite\Auth\Auth; use Appwrite\Detector\Detector; -use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -11,7 +10,6 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; -use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; use Utopia\App; @@ -31,8 +29,6 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Index as IndexValidator; -use Utopia\Database\Validator\Key; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Cursor; @@ -41,395 +37,10 @@ use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; use Utopia\Validator\ArrayList; -use Utopia\Validator\Boolean; use Utopia\Validator\JSON; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -App::post('/v1/databases/:databaseId/tables/:tableId/indexes') - ->alias('/v1/databases/:databaseId/collections/:tableId/indexes') - ->desc('Create index') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'index.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'tables', - name: 'createIndex', - description: '/docs/references/databases/create-index.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_ACCEPTED, - model: Response::MODEL_INDEX, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', null, new Key(), 'Index Key.') - ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') - ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') - ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $count = $dbForProject->count('indexes', [ - Query::equal('collectionInternalId', [$table->getInternalId()]), - Query::equal('databaseInternalId', [$db->getInternalId()]) - ], 61); - - $limit = $dbForProject->getLimitForIndexes(); - - if ($count >= $limit) { - throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded'); - } - - // Convert Document[] to array of attribute metadata - $oldColumns = \array_map(fn ($a) => $a->getArrayCopy(), $table->getAttribute('attributes')); - - $oldColumns[] = [ - 'key' => '$id', - 'type' => Database::VAR_STRING, - 'status' => 'available', - 'required' => true, - 'array' => false, - 'default' => null, - 'size' => Database::LENGTH_KEY - ]; - - $oldColumns[] = [ - 'key' => '$createdAt', - 'type' => Database::VAR_DATETIME, - 'status' => 'available', - 'signed' => false, - 'required' => false, - 'array' => false, - 'default' => null, - 'size' => 0 - ]; - - $oldColumns[] = [ - 'key' => '$updatedAt', - 'type' => Database::VAR_DATETIME, - 'status' => 'available', - 'signed' => false, - 'required' => false, - 'array' => false, - 'default' => null, - 'size' => 0 - ]; - - // lengths hidden by default - $lengths = []; - - foreach ($columns as $i => $column) { - // find attribute metadata in collection document - $columnIndex = \array_search($column, array_column($oldColumns, 'key')); - - if ($columnIndex === false) { - throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown column: ' . $column . '. Verify the column name or create the column.'); - } - - $columnStatus = $oldColumns[$columnIndex]['status']; - $columnType = $oldColumns[$columnIndex]['type']; - $columnArray = $oldColumns[$columnIndex]['array'] ?? false; - - if ($columnType === Database::VAR_RELATIONSHIP) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Cannot create an index for a relationship column: ' . $oldColumns[$columnIndex]['key']); - } - - // ensure attribute is available - if ($columnStatus !== 'available') { - throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Column not available: ' . $oldColumns[$columnIndex]['key']); - } - - $lengths[$i] = null; - - if ($columnArray === true) { - $lengths[$i] = Database::ARRAY_INDEX_LENGTH; - $orders[$i] = null; - } - } - - $index = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), - 'key' => $key, - 'status' => 'processing', // processing, available, failed, deleting, stuck - 'databaseInternalId' => $db->getInternalId(), - 'databaseId' => $databaseId, - 'collectionInternalId' => $table->getInternalId(), - 'collectionId' => $tableId, - 'type' => $type, - 'attributes' => $columns, - 'lengths' => $lengths, - 'orders' => $orders, - ]); - - $validator = new IndexValidator( - $table->getAttribute('attributes'), - $dbForProject->getAdapter()->getMaxIndexLength(), - $dbForProject->getAdapter()->getInternalIndexesKeys(), - ); - if (!$validator->isValid($index)) { - throw new Exception(Exception::INDEX_INVALID, $validator->getDescription()); - } - - try { - $index = $dbForProject->createDocument('indexes', $index); - } catch (DuplicateException) { - throw new Exception(Exception::INDEX_ALREADY_EXISTS); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - - $queueForDatabase - ->setType(DATABASE_TYPE_CREATE_INDEX) - ->setDatabase($db) - ->setTable($table) - ->setRow($index); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('indexId', $index->getId()) - ->setContext('table', $table) - ->setContext('database', $db); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($index, Response::MODEL_INDEX); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/indexes') - ->alias('/v1/databases/:databaseId/collections/:tableId/indexes') - ->desc('List indexes') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'indexes', - name: 'listIndexes', - description: '/docs/references/databases/list-indexes.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_INDEX_LIST, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject) { - /** @var Document $database */ - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $queries = Query::parseQueries($queries); - - \array_push( - $queries, - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$tableId]), - ); - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - $cursor = reset($cursor); - - if ($cursor) { - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $indexId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ - Query::equal('collectionInternalId', [$table->getInternalId()]), - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('key', [$indexId]), - Query::limit(1) - ])); - - if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Index '{$indexId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument[0]); - } - - $filterQueries = Query::groupByType($queries)['filters']; - try { - $total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT); - $indexes = $dbForProject->find('indexes', $queries); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); - } - - $response->dynamic(new Document([ - 'total' => $total, - 'indexes' => $indexes, - ]), Response::MODEL_INDEX_LIST); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/indexes/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') - ->desc('Get index') - ->groups(['api', 'database']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'indexes', - name: 'getIndex', - description: '/docs/references/databases/get-index.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_INDEX, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', null, new Key(), 'Index Key.') - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $index = $table->find('key', $key, 'indexes'); - if (empty($index)) { - throw new Exception(Exception::INDEX_NOT_FOUND); - } - - $response->dynamic($index, Response::MODEL_INDEX); - }); - - -App::delete('/v1/databases/:databaseId/tables/:tableId/indexes/:key') - ->alias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') - ->desc('Delete index') - ->groups(['api', 'database']) - ->label('scope', 'collections.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') - ->label('audits.event', 'index.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('sdk', new Method( - namespace: 'databases', - group: 'indexes', - name: 'deleteIndex', - description: '/docs/references/databases/delete-index.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('key', '', new Key(), 'Index Key.') - ->inject('response') - ->inject('dbForProject') - ->inject('queueForDatabase') - ->inject('queueForEvents') - ->action(function (string $databaseId, string $tableId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - - if (empty($index->getId())) { - throw new Exception(Exception::INDEX_NOT_FOUND); - } - - // Only update status if removing available index - if ($index->getAttribute('status') === 'available') { - $index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_INDEX) - ->setDatabase($db) - ->setTable($table) - ->setRow($index); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('indexId', $index->getId()) - ->setContext('table', $table) - ->setContext('database', $db) - ->setPayload($response->output($index, Response::MODEL_INDEX)); - - $response->noContent(); - }); - App::post('/v1/databases/:databaseId/tables/:tableId/rows') ->alias('/v1/databases/:databaseId/collections/:tableId/documents') ->desc('Create row') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php new file mode 100644 index 0000000000..49cb7157b0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php @@ -0,0 +1,217 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes') + ->desc('Create index') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'index.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'tables', + name: 'createIndex', + description: '/docs/references/databases/create-index.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: UtopiaResponse::MODEL_INDEX, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', null, new Key(), 'Index Key.') + ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') + ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') + ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $count = $dbForProject->count('indexes', [ + Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('databaseInternalId', [$db->getInternalId()]) + ], 61); + + $limit = $dbForProject->getLimitForIndexes(); + + if ($count >= $limit) { + throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded'); + } + + // Convert Document[] to array of attribute metadata + $oldColumns = \array_map(fn ($a) => $a->getArrayCopy(), $table->getAttribute('attributes')); + + $oldColumns[] = [ + 'key' => '$id', + 'type' => Database::VAR_STRING, + 'status' => 'available', + 'required' => true, + 'array' => false, + 'default' => null, + 'size' => Database::LENGTH_KEY + ]; + + $oldColumns[] = [ + 'key' => '$createdAt', + 'type' => Database::VAR_DATETIME, + 'status' => 'available', + 'signed' => false, + 'required' => false, + 'array' => false, + 'default' => null, + 'size' => 0 + ]; + + $oldColumns[] = [ + 'key' => '$updatedAt', + 'type' => Database::VAR_DATETIME, + 'status' => 'available', + 'signed' => false, + 'required' => false, + 'array' => false, + 'default' => null, + 'size' => 0 + ]; + + // lengths hidden by default + $lengths = []; + + foreach ($columns as $i => $column) { + // find attribute metadata in collection document + $columnIndex = \array_search($column, array_column($oldColumns, 'key')); + + if ($columnIndex === false) { + throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown column: ' . $column . '. Verify the column name or create the column.'); + } + + $columnStatus = $oldColumns[$columnIndex]['status']; + $columnType = $oldColumns[$columnIndex]['type']; + $columnArray = $oldColumns[$columnIndex]['array'] ?? false; + + if ($columnType === Database::VAR_RELATIONSHIP) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Cannot create an index for a relationship column: ' . $oldColumns[$columnIndex]['key']); + } + + // ensure attribute is available + if ($columnStatus !== 'available') { + throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Column not available: ' . $oldColumns[$columnIndex]['key']); + } + + $lengths[$i] = null; + + if ($columnArray === true) { + $lengths[$i] = Database::ARRAY_INDEX_LENGTH; + $orders[$i] = null; + } + } + + $index = new Document([ + '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), + 'key' => $key, + 'status' => 'processing', // processing, available, failed, deleting, stuck + 'databaseInternalId' => $db->getInternalId(), + 'databaseId' => $databaseId, + 'collectionInternalId' => $table->getInternalId(), + 'collectionId' => $tableId, + 'type' => $type, + 'attributes' => $columns, + 'lengths' => $lengths, + 'orders' => $orders, + ]); + + $validator = new IndexValidator( + $table->getAttribute('attributes'), + $dbForProject->getAdapter()->getMaxIndexLength(), + $dbForProject->getAdapter()->getInternalIndexesKeys(), + ); + if (!$validator->isValid($index)) { + throw new Exception(Exception::INDEX_INVALID, $validator->getDescription()); + } + + try { + $index = $dbForProject->createDocument('indexes', $index); + } catch (DuplicateException) { + throw new Exception(Exception::INDEX_ALREADY_EXISTS); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + + $queueForDatabase + ->setType(DATABASE_TYPE_CREATE_INDEX) + ->setDatabase($db) + ->setTable($table) + ->setRow($index); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('indexId', $index->getId()) + ->setContext('table', $table) + ->setContext('database', $db); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($index, UtopiaResponse::MODEL_INDEX); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php new file mode 100644 index 0000000000..eeb772e0c3 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php @@ -0,0 +1,109 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') + ->desc('Delete index') + ->groups(['api', 'database']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') + ->label('audits.event', 'index.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'indexes', + name: 'deleteIndex', + description: '/docs/references/databases/delete-index.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Index Key.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); + + if (empty($index->getId())) { + throw new Exception(Exception::INDEX_NOT_FOUND); + } + + // Only update status if removing available index + if ($index->getAttribute('status') === 'available') { + $index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_INDEX) + ->setDatabase($db) + ->setTable($table) + ->setRow($index); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('indexId', $index->getId()) + ->setContext('table', $table) + ->setContext('database', $db) + ->setPayload($response->output($index, UtopiaResponse::MODEL_INDEX)); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php new file mode 100644 index 0000000000..72ab1a02b1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php @@ -0,0 +1,80 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') + ->desc('Get index') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'indexes', + name: 'getIndex', + description: '/docs/references/databases/get-index.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_INDEX, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', null, new Key(), 'Index Key.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $index = $table->find('key', $key, 'indexes'); + if (empty($index)) { + throw new Exception(Exception::INDEX_NOT_FOUND); + } + + $response->dynamic($index, UtopiaResponse::MODEL_INDEX); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php new file mode 100644 index 0000000000..4920b679c1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php @@ -0,0 +1,129 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes') + ->desc('List indexes') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'indexes', + name: 'listIndexes', + description: '/docs/references/databases/list-indexes.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_INDEX_LIST, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void + { + /** @var Document $database */ + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $queries = Query::parseQueries($queries); + + \array_push( + $queries, + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$tableId]), + ); + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + $cursor = reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $indexId = $cursor->getValue(); + $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ + Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('key', [$indexId]), + Query::limit(1) + ])); + + if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Index '{$indexId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument[0]); + } + + $filterQueries = Query::groupByType($queries)['filters']; + try { + $total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT); + $indexes = $dbForProject->find('indexes', $queries); + } catch (OrderException $e) { + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); + } + + $response->dynamic(new Document([ + 'total' => $total, + 'indexes' => $indexes, + ]), UtopiaResponse::MODEL_INDEX_LIST); + } +} From 76785d58c63420eadd82a9a2afbef44ddf18a958 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 17:16:52 +0530 Subject: [PATCH 015/173] fix: imports, lint. --- .../Platform/Modules/Databases/Http/Tables/Delete.php | 5 +++-- .../Platform/Modules/Databases/Http/Tables/Update.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php index b3120ef36d..92003f3eeb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -11,6 +11,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -58,12 +59,12 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$thiss, 'action']); + ->callback([$this, 'action']); } public function action(string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void { - $database = \Utopia\Database\Validator\Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php index 2db6c5d328..dc302ce47f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -70,7 +70,7 @@ class Update extends Action public function action(string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void { - $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } From fd7801fe26ff95aba9ba5d94a05e94f310151ceb Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 17:24:38 +0530 Subject: [PATCH 016/173] add: register index and table modules. --- .../Modules/Databases/Services/Http.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index de0de71e05..045dc6ff42 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -31,6 +31,16 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Get as GetDatabase; use Appwrite\Platform\Modules\Databases\Http\Databases\Logs\XList as ListDatabaseLogs; use Appwrite\Platform\Modules\Databases\Http\Databases\Update as UpdateDatabase; use Appwrite\Platform\Modules\Databases\Http\Databases\XList as ListDatabases; +use Appwrite\Platform\Modules\Databases\Http\Indexes\Create as CreateIndex; +use Appwrite\Platform\Modules\Databases\Http\Indexes\Delete as DeleteIndex; +use Appwrite\Platform\Modules\Databases\Http\Indexes\Get as GetIndex; +use Appwrite\Platform\Modules\Databases\Http\Indexes\XList as ListIndexes; +use Appwrite\Platform\Modules\Databases\Http\Tables\Create as CreateTable; +use Appwrite\Platform\Modules\Databases\Http\Tables\Delete as DeleteTable; +use Appwrite\Platform\Modules\Databases\Http\Tables\Get as GetTable; +use Appwrite\Platform\Modules\Databases\Http\Tables\Logs\XList as ListTableLogs; +use Appwrite\Platform\Modules\Databases\Http\Tables\Update as UpdateTable; +use Appwrite\Platform\Modules\Databases\Http\Tables\XList as ListTables; use Utopia\Platform\Service; class Http extends Service @@ -58,7 +68,12 @@ class Http extends Service private function registerTableActions(): void { - + $this->addAction(CreateTable::getName(), new CreateTable()); + $this->addAction(GetTable::getName(), new GetTable()); + $this->addAction(UpdateTable::getName(), new UpdateTable()); + $this->addAction(DeleteTable::getName(), new DeleteTable()); + $this->addAction(ListTables::getName(), new ListTables()); + $this->addAction(ListTableLogs::getName(), new ListTableLogs()); } private function registerColumnActions(): void @@ -111,7 +126,10 @@ class Http extends Service private function registerIndexActions(): void { - + $this->addAction(CreateIndex::getName(), new CreateIndex()); + $this->addAction(GetIndex::getName(), new GetIndex()); + $this->addAction(DeleteIndex::getName(), new DeleteIndex()); + $this->addAction(ListIndexes::getName(), new ListIndexes()); } private function registerRowActions(): void From 286c695b47c51a6608fe8723d431e8865b9c2069 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 17:52:29 +0530 Subject: [PATCH 017/173] add: move and register row modules. --- app/controllers/api/databases.php | 1087 ----------------- .../Modules/Databases/Http/Rows/Create.php | 314 +++++ .../Modules/Databases/Http/Rows/Delete.php | 167 +++ .../Modules/Databases/Http/Rows/Get.php | 154 +++ .../Databases/Http/Rows/Logs/XList.php | 159 +++ .../Modules/Databases/Http/Rows/Update.php | 302 +++++ .../Modules/Databases/Http/Rows/XList.php | 223 ++++ .../Modules/Databases/Services/Http.php | 13 +- 8 files changed, 1331 insertions(+), 1088 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 63a4e3fbb7..1f18d2fa1e 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1,1107 +1,20 @@ alias('/v1/databases/:databaseId/collections/:tableId/documents') - ->desc('Create row') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') - ->label('scope', 'documents.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'row.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') - ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') - ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) - ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) - ->label( - 'sdk', - [ - new Method( - namespace: 'databases', - group: 'rows', - name: 'createRow', - description: '/docs/references/databases/create-document.md', - auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_DOCUMENT, - ) - ], - contentType: ContentType::JSON - ) - ] - ) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.') - ->param('data', [], new JSON(), 'Row data as JSON object.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->inject('response') - ->inject('dbForProject') - ->inject('user') - ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - - $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array - - if (empty($data)) { - throw new Exception(Exception::DOCUMENT_MISSING_DATA); - } - - if (isset($data['$id'])) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); - } - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $allowedPermissions = [ - Database::PERMISSION_READ, - Database::PERMISSION_UPDATE, - Database::PERMISSION_DELETE, - ]; - - // Map aggregate permissions to into the set of individual permissions they represent. - $permissions = Permission::aggregate($permissions, $allowedPermissions); - - // Add permissions for current the user if none were provided. - if (\is_null($permissions)) { - $permissions = []; - if (!empty($user->getId())) { - foreach ($allowedPermissions as $permission) { - $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); - } - } - } - - // Users can only manage their own roles, API keys and Admin users can manage any - if (!$isAPIKey && !$isPrivilegedUser) { - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); - } - } - } - } - - $data['$collection'] = $table->getId(); // Adding this param to make API easier for developers - $data['$id'] = $rowId == 'unique()' ? ID::unique() : $rowId; - $data['$permissions'] = $permissions; - $row = new Document($data); - - $operations = 0; - - $checkPermissions = function (Document $table, Document $row, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) { - $operations++; - - $documentSecurity = $table->getAttribute('documentSecurity', false); - $validator = new Authorization($permission); - - $valid = $validator->isValid($table->getPermissionsByType($permission)); - if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - - if ($permission === Database::PERMISSION_UPDATE) { - $valid = $valid || $validator->isValid($row->getUpdate()); - if ($documentSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - } - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - - $isList = \is_array($related) && \array_values($related) === $related; - - if ($isList) { - $relations = $related; - } else { - $relations = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($relations as &$relation) { - if ( - \is_array($relation) - && \array_values($relation) !== $relation - && !isset($relation['$id']) - ) { - $relation['$id'] = ID::unique(); - $relation = new Document($relation); - } - if ($relation instanceof Document) { - $current = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId()) - ); - - if ($current->isEmpty()) { - $type = Database::PERMISSION_CREATE; - - if (isset($relation['$id']) && $relation['$id'] === 'unique()') { - $relation['$id'] = ID::unique(); - } - } else { - $relation->removeAttribute('$collectionId'); - $relation->removeAttribute('$databaseId'); - $relation->setAttribute('$collection', $relatedTable->getId()); - $type = Database::PERMISSION_UPDATE; - } - - $checkPermissions($relatedTable, $relation, $type); - } - } - - if ($isList) { - $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } else { - $row->setAttribute($relationship->getAttribute('key'), \reset($relations)); - } - } - }; - - $checkPermissions($table, $row, Database::PERMISSION_CREATE); - - try { - $row = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $row); - } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); - } catch (DuplicateException $e) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); - } catch (NotFoundException $e) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection - - $response->addHeader('X-Debug-Operations', $operations); - - $response - ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($row, Response::MODEL_DOCUMENT); - - $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), - \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('rowId', $row->getId()) - ->setContext('table', $table) - ->setContext('database', $database) - ->setPayload($response->getPayload(), sensitive: $relationships); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/rows') - ->alias('/v1/databases/:databaseId/collections/:tableId/documents') - ->desc('List rows') - ->groups(['api', 'database']) - ->label('scope', 'documents.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'rows', - name: 'listRows', - description: '/docs/references/databases/list-documents.md', - auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DOCUMENT_LIST, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - - $cursor = \reset($cursor); - - if ($cursor) { - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $rowId = $cursor->getValue(); - - $cursorRow = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - - if ($cursorRow->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Row '{$rowId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorRow); - } - try { - $rows = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); - } - - $operations = 0; - - // Add $tableId and $databaseId for all rows - $processRow = (function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations): bool { - if ($row->isEmpty()) { - return false; - } - - $operations++; - - $row->removeAttribute('$collection'); - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { - $operations++; - } - - continue; - } - - if (!\is_array($related)) { - $relations = [$related]; - } else { - $relations = $related; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - // todo: Use local cache for this getDocument - $relatedTable = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)); - - foreach ($relations as $index => $doc) { - if ($doc instanceof Document) { - if (!$processRow($relatedTable, $doc)) { - unset($relations[$index]); - } - } - } - - if (\is_array($related)) { - $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } elseif (empty($relations)) { - $row->setAttribute($relationship->getAttribute('key'), null); - } - } - - return true; - }); - - foreach ($rows as $row) { - $processRow($table, $row); - } - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); - - $response->addHeader('X-Debug-Operations', $operations); - - $select = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT); - }, false); - - // Check if the SELECT query includes $databaseId and $collectionId - $hasDatabaseId = false; - $hasTableId = false; - if ($select) { - $hasDatabaseId = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues())); - }, false); - $hasTableId = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues())); - }, false); - } - - if ($select) { - foreach ($rows as $row) { - if (!$hasDatabaseId) { - $row->removeAttribute('$databaseId'); - } - if (!$hasTableId) { - $row->removeAttribute('$collectionId'); - } - } - } - - $response->dynamic(new Document([ - 'total' => $total, - 'documents' => $rows, - ]), Response::MODEL_DOCUMENT_LIST); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') - ->desc('Get row') - ->groups(['api', 'database']) - ->label('scope', 'documents.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'rows', - name: 'getRow', - description: '/docs/references/databases/get-document.md', - auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DOCUMENT, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('rowId', '', new UID(), 'Row ID.') - ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->inject('response') - ->inject('dbForProject') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $tableId, string $rowId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId, $queries); - } catch (AuthorizationException) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } - - $operations = 0; - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations) { - if ($row->isEmpty()) { - return; - } - - $operations++; - - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { - $operations++; - } - - continue; - } - - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); - - $response->addHeader('X-Debug-Operations', $operations); - - $response->dynamic($row, Response::MODEL_DOCUMENT); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs') - ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId/logs') - ->desc('List row logs') - ->groups(['api', 'database']) - ->label('scope', 'documents.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: 'logs', - name: 'listRowLogs', - description: '/docs/references/databases/get-document-logs.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_LOG_LIST, - ) - ], - contentType: ContentType::JSON, - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Collection ID.') - ->param('rowId', '', new UID(), 'Row ID.') - ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) - ->inject('response') - ->inject('dbForProject') - ->inject('locale') - ->inject('geodb') - ->action(function (string $databaseId, string $tableId, string $rowId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId); - - if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - // Temp fix for logs - $queries[] = Query::or([ - Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), - Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), - ]); - - $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/table/' . $tableId . '/row/' . $row->getId(); - $logs = $audit->getLogsByResource($resource, $queries); - - $output = []; - - foreach ($logs as $i => &$log) { - $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - - $detector = new Detector($log['userAgent']); - $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - - $os = $detector->getOS(); - $client = $detector->getClient(); - $device = $detector->getDevice(); - - $output[$i] = new Document([ - 'event' => $log['event'], - 'userId' => $log['data']['userId'], - 'userEmail' => $log['data']['userEmail'] ?? null, - 'userName' => $log['data']['userName'] ?? null, - 'mode' => $log['data']['mode'] ?? null, - 'ip' => $log['ip'], - 'time' => $log['time'], - 'osCode' => $os['osCode'], - 'osName' => $os['osName'], - 'osVersion' => $os['osVersion'], - 'clientType' => $client['clientType'], - 'clientCode' => $client['clientCode'], - 'clientName' => $client['clientName'], - 'clientVersion' => $client['clientVersion'], - 'clientEngine' => $client['clientEngine'], - 'clientEngineVersion' => $client['clientEngineVersion'], - 'deviceName' => $device['deviceName'], - 'deviceBrand' => $device['deviceBrand'], - 'deviceModel' => $device['deviceModel'] - ]); - - $record = $geodb->get($log['ip']); - - if ($record) { - $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; - $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); - } else { - $output[$i]['countryCode'] = '--'; - $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); - } - } - - $response->dynamic(new Document([ - 'total' => $audit->countLogsByResource($resource, $queries), - 'logs' => $output, - ]), Response::MODEL_LOG_LIST); - }); - -App::patch('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') - ->desc('Update row') - ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].update') - ->label('scope', 'documents.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'row.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') - ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') - ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) - ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) - ->label('sdk', new Method( - namespace: 'databases', - group: 'rows', - name: 'updateRow', - description: '/docs/references/databases/update-document.md', - auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_DOCUMENT, - ) - ], - contentType: ContentType::JSON - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Collection ID.') - ->param('rowId', '', new UID(), 'Row ID.') - ->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->inject('requestTimestamp') - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - - $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array - - if (empty($data) && \is_null($permissions)) { - throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); - } - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - // Read permission should not be required for update - /** @var Document $row */ - $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - - if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } - - // Map aggregate permissions into the multiple permissions they represent. - $permissions = Permission::aggregate($permissions, [ - Database::PERMISSION_READ, - Database::PERMISSION_UPDATE, - Database::PERMISSION_DELETE, - ]); - - // Users can only manage their own roles, API keys and Admin users can manage any - $roles = Authorization::getRoles(); - if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); - } - } - } - } - - if (\is_null($permissions)) { - $permissions = $row->getPermissions() ?? []; - } - - $data['$id'] = $rowId; - $data['$permissions'] = $permissions; - $newRow = new Document($data); - - $operations = 0; - - $setTable = (function (Document $collection, Document $document) use (&$setTable, $dbForProject, $database, &$operations) { - - $operations++; - - $relationships = \array_filter( - $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - - $isList = \is_array($related) && \array_values($related) === $related; - - if ($isList) { - $relations = $related; - } else { - $relations = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($relations as &$relation) { - // If the relation is an array it can be either update or create a child document. - if ( - \is_array($relation) - && \array_values($relation) !== $relation - && !isset($relation['$id']) - ) { - $relation['$id'] = ID::unique(); - $relation = new Document($relation); - } - if ($relation instanceof Document) { - $oldRow = Authorization::skip(fn () => $dbForProject->getDocument( - 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), - $relation->getId() - )); - $relation->removeAttribute('$collectionId'); - $relation->removeAttribute('$databaseId'); - // Attribute $collection is required for Utopia. - $relation->setAttribute( - '$collection', - 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId() - ); - - if ($oldRow->isEmpty()) { - if (isset($relation['$id']) && $relation['$id'] === 'unique()') { - $relation['$id'] = ID::unique(); - } - } - $setTable($relatedTable, $relation); - } - } - - if ($isList) { - $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } else { - $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); - } - } - }); - - $setTable($table, $newRow); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); - - $response->addHeader('X-Debug-Operations', $operations); - - try { - $row = $dbForProject->withRequestTimestamp( - $requestTimestamp, - fn () => $dbForProject->updateDocument( - 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), - $row->getId(), - $newRow - ) - ); - } catch (AuthorizationException) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); - } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); - } catch (NotFoundException $e) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $response->dynamic($row, Response::MODEL_DOCUMENT); - - $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), - \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('rowId', $row->getId()) - ->setContext('table', $table) - ->setContext('database', $database) - ->setPayload($response->getPayload(), sensitive: $relationships); - }); - -App::delete('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') - ->desc('Delete row') - ->groups(['api', 'database']) - ->label('scope', 'documents.write') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].delete') - ->label('audits.event', 'row.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{request.rowId}') - ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') - ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) - ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) - ->label('sdk', new Method( - namespace: 'databases', - group: 'rows', - name: 'deleteRow', - description: '/docs/references/databases/delete-document.md', - auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('rowId', '', new UID(), 'Row ID.') - ->inject('requestTimestamp') - ->inject('response') - ->inject('dbForProject') - ->inject('queueForEvents') - ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - // Read permission should not be required for delete - $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - - if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } - - try { - $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $table, $rowId) { - $dbForProject->deleteDocument( - 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), - $rowId - ); - }); - } catch (NotFoundException $e) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection - - $response->addHeader('X-Debug-Operations', 1); - - $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), - \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('rowId', $row->getId()) - ->setContext('table', $table) - ->setContext('database', $database) - ->setPayload($response->output($row, Response::MODEL_DOCUMENT), sensitive: $relationships); - - $response->noContent(); - }); - App::get('/v1/databases/usage') ->desc('Get databases usage stats') ->groups(['api', 'database', 'usage']) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php new file mode 100644 index 0000000000..ab40f4d5e1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -0,0 +1,314 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents') + ->desc('Create row') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'row.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', [ + new Method( + namespace: 'databases', + group: 'rows', + name: 'createRow', + description: '/docs/references/databases/create-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: UtopiaResponse::MODEL_DOCUMENT, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.') + ->param('data', [], new JSON(), 'Row data as JSON object.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('response') + ->inject('dbForProject') + ->inject('user') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + + if (empty($data)) { + throw new Exception(Exception::DOCUMENT_MISSING_DATA); + } + + if (isset($data['$id'])) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); + + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $allowedPermissions = [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]; + + // Map aggregate permissions to into the set of individual permissions they represent. + $permissions = Permission::aggregate($permissions, $allowedPermissions); + + // Add permissions for current the user if none were provided. + if (\is_null($permissions)) { + $permissions = []; + if (!empty($user->getId())) { + foreach ($allowedPermissions as $permission) { + $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); + } + } + } + + // Users can only manage their own roles, API keys and Admin users can manage any + if (!$isAPIKey && !$isPrivilegedUser) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); + } + } + } + } + + $data['$collection'] = $table->getId(); // Adding this param to make API easier for developers + $data['$id'] = $rowId == 'unique()' ? ID::unique() : $rowId; + $data['$permissions'] = $permissions; + $row = new Document($data); + + $operations = 0; + + $checkPermissions = function (Document $table, Document $row, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) { + $operations++; + + $documentSecurity = $table->getAttribute('documentSecurity', false); + $validator = new Authorization($permission); + + $valid = $validator->isValid($table->getPermissionsByType($permission)); + if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + + if ($permission === Database::PERMISSION_UPDATE) { + $valid = $valid || $validator->isValid($row->getUpdate()); + if ($documentSecurity && !$valid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + } + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($relations as &$relation) { + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } + if ($relation instanceof Document) { + $current = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId()) + ); + + if ($current->isEmpty()) { + $type = Database::PERMISSION_CREATE; + + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { + $relation['$id'] = ID::unique(); + } + } else { + $relation->removeAttribute('$collectionId'); + $relation->removeAttribute('$databaseId'); + $relation->setAttribute('$collection', $relatedTable->getId()); + $type = Database::PERMISSION_UPDATE; + } + + $checkPermissions($relatedTable, $relation, $type); + } + } + + if ($isList) { + $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $row->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } + } + }; + + $checkPermissions($table, $row, Database::PERMISSION_CREATE); + + try { + $row = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $row); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (NotFoundException) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processRow($relatedTable, $relation); + } + } + } + }; + + $processRow($table, $row); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection + + $response->addHeader('X-Debug-Operations', $operations); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($row, UtopiaResponse::MODEL_DOCUMENT); + + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('rowId', $row->getId()) + ->setContext('table', $table) + ->setContext('database', $database) + ->setPayload($response->getPayload(), sensitive: $relationships); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php new file mode 100644 index 0000000000..61ed961c64 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php @@ -0,0 +1,167 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') + ->desc('Delete row') + ->groups(['api', 'database']) + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].delete') + ->label('audits.event', 'row.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{request.rowId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: 'rows', + name: 'deleteRow', + description: '/docs/references/databases/delete-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('rowId', '', new UID(), 'Row ID.') + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); + + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + // Read permission should not be required for delete + $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); + + if ($row->isEmpty()) { + throw new Exception(Exception::DOCUMENT_NOT_FOUND); + } + + try { + $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $table, $rowId) { + $dbForProject->deleteDocument( + 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), + $rowId + ); + }); + } catch (NotFoundException) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processRow($relatedTable, $relation); + } + } + } + }; + + $processRow($table, $row); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection + + $response->addHeader('X-Debug-Operations', 1); + + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('rowId', $row->getId()) + ->setContext('table', $table) + ->setContext('database', $database) + ->setPayload($response->output($row, UtopiaResponse::MODEL_DOCUMENT), sensitive: $relationships); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php new file mode 100644 index 0000000000..a3a484be70 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -0,0 +1,154 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') + ->desc('Get row') + ->groups(['api', 'database']) + ->label('scope', 'documents.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'rows', + name: 'getRow', + description: '/docs/references/databases/get-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_DOCUMENT, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); + + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + try { + $queries = Query::parseQueries($queries); + $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId, $queries); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + if ($row->isEmpty()) { + throw new Exception(Exception::DOCUMENT_NOT_FOUND); + } + + $operations = 0; + + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations) { + if ($row->isEmpty()) { + return; + } + + $operations++; + + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + if (\in_array(\gettype($related), ['array', 'object'])) { + $operations++; + } + + continue; + } + + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processRow($relatedTable, $relation); + } + } + } + }; + + $processRow($table, $row); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); + + $response->addHeader('X-Debug-Operations', $operations); + + $response->dynamic($row, UtopiaResponse::MODEL_DOCUMENT); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php new file mode 100644 index 0000000000..02358be8f2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php @@ -0,0 +1,159 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId/logs') + ->desc('List row logs') + ->groups(['api', 'database']) + ->label('scope', 'documents.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'logs', + name: 'listRowLogs', + description: '/docs/references/databases/get-document-logs.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_LOG_LIST, + ) + ], + contentType: ContentType::JSON, + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Collection ID.') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) + ->inject('response') + ->inject('dbForProject') + ->inject('locale') + ->inject('geodb') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId); + + if ($row->isEmpty()) { + throw new Exception(Exception::DOCUMENT_NOT_FOUND); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + // Temp fix for logs + $queries[] = Query::or([ + Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), + Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), + ]); + + $audit = new Audit($dbForProject); + $resource = 'database/' . $databaseId . '/table/' . $tableId . '/row/' . $row->getId(); + $logs = $audit->getLogsByResource($resource, $queries); + + $output = []; + + foreach ($logs as $i => &$log) { + $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; + + $detector = new Detector($log['userAgent']); + $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) + + $os = $detector->getOS(); + $client = $detector->getClient(); + $device = $detector->getDevice(); + + $output[$i] = new Document([ + 'event' => $log['event'], + 'userId' => $log['data']['userId'], + 'userEmail' => $log['data']['userEmail'] ?? null, + 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'ip' => $log['ip'], + 'time' => $log['time'], + 'osCode' => $os['osCode'], + 'osName' => $os['osName'], + 'osVersion' => $os['osVersion'], + 'clientType' => $client['clientType'], + 'clientCode' => $client['clientCode'], + 'clientName' => $client['clientName'], + 'clientVersion' => $client['clientVersion'], + 'clientEngine' => $client['clientEngine'], + 'clientEngineVersion' => $client['clientEngineVersion'], + 'deviceName' => $device['deviceName'], + 'deviceBrand' => $device['deviceBrand'], + 'deviceModel' => $device['deviceModel'] + ]); + + $record = $geodb->get($log['ip']); + + if ($record) { + $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; + $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); + } else { + $output[$i]['countryCode'] = '--'; + $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); + } + } + + $response->dynamic(new Document([ + 'total' => $audit->countLogsByResource($resource, $queries), + 'logs' => $output, + ]), UtopiaResponse::MODEL_LOG_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php new file mode 100644 index 0000000000..bc41118206 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -0,0 +1,302 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') + ->desc('Update row') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].update') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'row.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: 'rows', + name: 'updateRow', + description: '/docs/references/databases/update-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_DOCUMENT, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Collection ID.') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true) + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + + if (empty($data) && \is_null($permissions)) { + throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); + + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + // Read permission should not be required for update + /** @var Document $row */ + $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); + + if ($row->isEmpty()) { + throw new Exception(Exception::DOCUMENT_NOT_FOUND); + } + + // Map aggregate permissions into the multiple permissions they represent. + $permissions = Permission::aggregate($permissions, [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]); + + // Users can only manage their own roles, API keys and Admin users can manage any + $roles = Authorization::getRoles(); + if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + } + } + } + } + + if (\is_null($permissions)) { + $permissions = $row->getPermissions() ?? []; + } + + $data['$id'] = $rowId; + $data['$permissions'] = $permissions; + $newRow = new Document($data); + + $operations = 0; + + $setTable = (function (Document $collection, Document $document) use (&$setTable, $dbForProject, $database, &$operations) { + + $operations++; + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($relations as &$relation) { + // If the relation is an array it can be either update or create a child document. + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } + if ($relation instanceof Document) { + $oldRow = Authorization::skip(fn () => $dbForProject->getDocument( + 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), + $relation->getId() + )); + $relation->removeAttribute('$collectionId'); + $relation->removeAttribute('$databaseId'); + // Attribute $collection is required for Utopia. + $relation->setAttribute( + '$collection', + 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId() + ); + + if ($oldRow->isEmpty()) { + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { + $relation['$id'] = ID::unique(); + } + } + $setTable($relatedTable, $relation); + } + } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } + } + }); + + $setTable($table, $newRow); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); + + $response->addHeader('X-Debug-Operations', $operations); + + try { + $row = $dbForProject->withRequestTimestamp( + $requestTimestamp, + fn () => $dbForProject->updateDocument( + 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), + $row->getId(), + $newRow + ) + ); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } catch (NotFoundException) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + // Add $tableId and $databaseId for all rows + $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processRow($relatedTable, $relation); + } + } + } + }; + + $processRow($table, $row); + + $response->dynamic($row, UtopiaResponse::MODEL_DOCUMENT); + + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('tableId', $table->getId()) + ->setParam('rowId', $row->getId()) + ->setContext('table', $table) + ->setContext('database', $database) + ->setPayload($response->getPayload(), sensitive: $relationships); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php new file mode 100644 index 0000000000..b27930405f --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -0,0 +1,223 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents') + ->desc('List rows') + ->groups(['api', 'database']) + ->label('scope', 'documents.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'rows', + name: 'listRows', + description: '/docs/references/databases/list-documents.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_DOCUMENT_LIST, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); + + if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + + $cursor = \reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $rowId = $cursor->getValue(); + + $cursorRow = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); + + if ($cursorRow->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Row '{$rowId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorRow); + } + try { + $rows = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries, APP_LIMIT_COUNT); + } catch (OrderException $e) { + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); + } + + $operations = 0; + + // Add $tableId and $databaseId for all rows + $processRow = (function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations): bool { + if ($row->isEmpty()) { + return false; + } + + $operations++; + + $row->removeAttribute('$collection'); + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + if (\in_array(\gettype($related), ['array', 'object'])) { + $operations++; + } + + continue; + } + + if (!\is_array($related)) { + $relations = [$related]; + } else { + $relations = $related; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + // todo: Use local cache for this getDocument + $relatedTable = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)); + + foreach ($relations as $index => $doc) { + if ($doc instanceof Document) { + if (!$processRow($relatedTable, $doc)) { + unset($relations[$index]); + } + } + } + + if (\is_array($related)) { + $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } elseif (empty($relations)) { + $row->setAttribute($relationship->getAttribute('key'), null); + } + } + + return true; + }); + + foreach ($rows as $row) { + $processRow($table, $row); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); + + $response->addHeader('X-Debug-Operations', $operations); + + $select = \array_reduce($queries, function ($result, $query) { + return $result || ($query->getMethod() === Query::TYPE_SELECT); + }, false); + + // Check if the SELECT query includes $databaseId and $collectionId + $hasDatabaseId = false; + $hasTableId = false; + if ($select) { + $hasDatabaseId = \array_reduce($queries, function ($result, $query) { + return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues())); + }, false); + $hasTableId = \array_reduce($queries, function ($result, $query) { + return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues())); + }, false); + } + + if ($select) { + foreach ($rows as $row) { + if (!$hasDatabaseId) { + $row->removeAttribute('$databaseId'); + } + if (!$hasTableId) { + $row->removeAttribute('$collectionId'); + } + } + } + + $response->dynamic(new Document([ + 'total' => $total, + 'documents' => $rows, + ]), UtopiaResponse::MODEL_DOCUMENT_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index 045dc6ff42..17dd715906 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -35,6 +35,12 @@ use Appwrite\Platform\Modules\Databases\Http\Indexes\Create as CreateIndex; use Appwrite\Platform\Modules\Databases\Http\Indexes\Delete as DeleteIndex; use Appwrite\Platform\Modules\Databases\Http\Indexes\Get as GetIndex; use Appwrite\Platform\Modules\Databases\Http\Indexes\XList as ListIndexes; +use Appwrite\Platform\Modules\Databases\Http\Rows\Create as CreateRow; +use Appwrite\Platform\Modules\Databases\Http\Rows\Delete as DeleteRow; +use Appwrite\Platform\Modules\Databases\Http\Rows\Get as GetRow; +use Appwrite\Platform\Modules\Databases\Http\Rows\Logs\XList as ListRowLogs; +use Appwrite\Platform\Modules\Databases\Http\Rows\Update as UpdateRow; +use Appwrite\Platform\Modules\Databases\Http\Rows\XList as ListRows; use Appwrite\Platform\Modules\Databases\Http\Tables\Create as CreateTable; use Appwrite\Platform\Modules\Databases\Http\Tables\Delete as DeleteTable; use Appwrite\Platform\Modules\Databases\Http\Tables\Get as GetTable; @@ -134,6 +140,11 @@ class Http extends Service private function registerRowActions(): void { - + $this->addAction(CreateRow::getName(), new CreateRow()); + $this->addAction(GetRow::getName(), new GetRow()); + $this->addAction(UpdateRow::getName(), new UpdateRow()); + $this->addAction(DeleteRow::getName(), new DeleteRow()); + $this->addAction(ListRows::getName(), new ListRows()); + $this->addAction(ListRowLogs::getName(), new ListRowLogs()); } } From 4cd8f49f829aa2612ccfff40884f744f4fa2e078 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:09:39 +0530 Subject: [PATCH 018/173] add: usage. --- .../Databases/Http/Databases/Usage/Get.php | 138 ++++++++++++++++++ .../Databases/Http/Databases/Usage/XList.php | 132 +++++++++++++++++ .../Databases/Http/Tables/Usage/Get.php | 131 +++++++++++++++++ .../Modules/Databases/Services/Http.php | 6 + 4 files changed, 407 insertions(+) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php new file mode 100644 index 0000000000..c4105effa0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -0,0 +1,138 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/usage') + ->desc('Get database usage stats') + ->groups(['api', 'database', 'usage']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: null, + name: 'getUsage', + description: '/docs/references/databases/get-database-usage.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_USAGE_DATABASE, + ) + ], + contentType: ContentType::JSON, + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $range, UtopiaResponse $response, Database $dbForProject): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE), + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_READS), + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_WRITES) + ]; + + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $result = $dbForProject->findOne('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', ['inf']) + ]); + + $stats[$metric]['total'] = $result['value'] ?? 0; + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', [$period]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric]['data'] = []; + foreach ($results as $result) { + $stats[$metric]['data'][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; + } + } + }); + + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['data'] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric]['data'][] = [ + 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; + } + } + + $response->dynamic(new Document([ + 'range' => $range, + 'collectionsTotal' => $usage[$metrics[0]]['total'], + 'documentsTotal' => $usage[$metrics[1]]['total'], + 'storageTotal' => $usage[$metrics[2]]['total'], + 'databaseReadsTotal' => $usage[$metrics[3]]['total'], + 'databaseWritesTotal' => $usage[$metrics[4]]['total'], + 'collections' => $usage[$metrics[0]]['data'], + 'documents' => $usage[$metrics[1]]['data'], + 'storage' => $usage[$metrics[2]]['data'], + 'databaseReads' => $usage[$metrics[3]]['data'], + 'databaseWrites' => $usage[$metrics[4]]['data'], + ]), UtopiaResponse::MODEL_USAGE_DATABASE); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php new file mode 100644 index 0000000000..0078d54a4b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -0,0 +1,132 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/usage') + ->desc('Get databases usage stats') + ->groups(['api', 'database', 'usage']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: null, + name: 'listUsages', + description: '/docs/references/databases/get-usage.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_USAGE_DATABASES, + ) + ], + contentType: ContentType::JSON + )) + ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $range, UtopiaResponse $response, Database $dbForProject): void + { + + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + METRIC_DATABASES, + METRIC_COLLECTIONS, + METRIC_DOCUMENTS, + METRIC_DATABASES_STORAGE, + METRIC_DATABASES_OPERATIONS_READS, + METRIC_DATABASES_OPERATIONS_WRITES, + ]; + + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $result = $dbForProject->findOne('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', ['inf']) + ]); + + $stats[$metric]['total'] = $result['value'] ?? 0; + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', [$period]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric]['data'] = []; + foreach ($results as $result) { + $stats[$metric]['data'][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; + } + } + }); + + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['data'] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric]['data'][] = [ + 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; + } + } + $response->dynamic(new Document([ + 'range' => $range, + 'databasesTotal' => $usage[$metrics[0]]['total'], + 'collectionsTotal' => $usage[$metrics[1]]['total'], + 'documentsTotal' => $usage[$metrics[2]]['total'], + 'storageTotal' => $usage[$metrics[3]]['total'], + 'databasesReadsTotal' => $usage[$metrics[4]]['total'], + 'databasesWritesTotal' => $usage[$metrics[5]]['total'], + 'databases' => $usage[$metrics[0]]['data'], + 'collections' => $usage[$metrics[1]]['data'], + 'documents' => $usage[$metrics[2]]['data'], + 'storage' => $usage[$metrics[3]]['data'], + 'databasesReads' => $usage[$metrics[4]]['data'], + 'databasesWrites' => $usage[$metrics[5]]['data'], + ]), UtopiaResponse::MODEL_USAGE_DATABASES); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php new file mode 100644 index 0000000000..3943c27618 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -0,0 +1,131 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/usage') + ->httpAlias('/v1/databases/:databaseId/collections/:tableId/usage') + ->desc('Get table usage stats') + ->groups(['api', 'database', 'usage']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: null, + name: 'getTableUsage', + description: '/docs/references/databases/get-collection-usage.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: UtopiaResponse::MODEL_USAGE_COLLECTION, + ) + ], + contentType: ContentType::JSON, + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) + ->param('tableId', '', new UID(), 'Collection ID.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $range, string $tableId, UtopiaResponse $response, Database $dbForProject): void + { + + $database = $dbForProject->getDocument('databases', $databaseId); + $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); + + if ($table->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $tableDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), + ]; + + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $result = $dbForProject->findOne('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', ['inf']) + ]); + + $stats[$metric]['total'] = $result['value'] ?? 0; + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', [$period]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric]['data'] = []; + foreach ($results as $result) { + $stats[$metric]['data'][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; + } + } + }); + + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['data'] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric]['data'][] = [ + 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; + } + } + + $response->dynamic(new Document([ + 'range' => $range, + 'documentsTotal' => $usage[$metrics[0]]['total'], + 'documents' => $usage[$metrics[0]]['data'], + ]), UtopiaResponse::MODEL_USAGE_COLLECTION); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index 17dd715906..ffdd4f1903 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -30,6 +30,8 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Delete as DeleteDatabase; use Appwrite\Platform\Modules\Databases\Http\Databases\Get as GetDatabase; use Appwrite\Platform\Modules\Databases\Http\Databases\Logs\XList as ListDatabaseLogs; use Appwrite\Platform\Modules\Databases\Http\Databases\Update as UpdateDatabase; +use Appwrite\Platform\Modules\Databases\Http\Databases\Usage\Get as GetDatabaseUsage; +use Appwrite\Platform\Modules\Databases\Http\Databases\Usage\XList as ListDatabaseUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\XList as ListDatabases; use Appwrite\Platform\Modules\Databases\Http\Indexes\Create as CreateIndex; use Appwrite\Platform\Modules\Databases\Http\Indexes\Delete as DeleteIndex; @@ -46,6 +48,7 @@ use Appwrite\Platform\Modules\Databases\Http\Tables\Delete as DeleteTable; use Appwrite\Platform\Modules\Databases\Http\Tables\Get as GetTable; use Appwrite\Platform\Modules\Databases\Http\Tables\Logs\XList as ListTableLogs; use Appwrite\Platform\Modules\Databases\Http\Tables\Update as UpdateTable; +use Appwrite\Platform\Modules\Databases\Http\Tables\Usage\Get as GetTableUsage; use Appwrite\Platform\Modules\Databases\Http\Tables\XList as ListTables; use Utopia\Platform\Service; @@ -70,6 +73,8 @@ class Http extends Service $this->addAction(DeleteDatabase::getName(), new DeleteDatabase()); $this->addAction(ListDatabases::getName(), new ListDatabases()); $this->addAction(ListDatabaseLogs::getName(), new ListDatabaseLogs()); + $this->addAction(GetDatabaseUsage::getName(), new GetDatabaseUsage()); + $this->addAction(ListDatabaseUsage::getName(), new ListDatabaseUsage()); } private function registerTableActions(): void @@ -80,6 +85,7 @@ class Http extends Service $this->addAction(DeleteTable::getName(), new DeleteTable()); $this->addAction(ListTables::getName(), new ListTables()); $this->addAction(ListTableLogs::getName(), new ListTableLogs()); + $this->addAction(GetTableUsage::getName(), new GetTableUsage()); } private function registerColumnActions(): void From 57a9218649a3185581943e7f2e17c16a612fa5f4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:10:09 +0530 Subject: [PATCH 019/173] =?UTF-8?q?remove:=20database=20controller=20?= =?UTF-8?q?=F0=9F=91=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/databases.php | 306 ------------------------------ 1 file changed, 306 deletions(-) delete mode 100644 app/controllers/api/databases.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php deleted file mode 100644 index 1f18d2fa1e..0000000000 --- a/app/controllers/api/databases.php +++ /dev/null @@ -1,306 +0,0 @@ -desc('Get databases usage stats') - ->groups(['api', 'database', 'usage']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: null, - name: 'getUsage', - description: '/docs/references/databases/get-usage.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_USAGE_DATABASES, - ) - ], - contentType: ContentType::JSON - )) - ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) - ->inject('response') - ->inject('dbForProject') - ->action(function (string $range, Response $response, Database $dbForProject) { - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_DATABASES, - METRIC_COLLECTIONS, - METRIC_DOCUMENTS, - METRIC_DATABASES_STORAGE, - METRIC_DATABASES_OPERATIONS_READS, - METRIC_DATABASES_OPERATIONS_WRITES, - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', ['inf']) - ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', [$period]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric]['data'] = []; - foreach ($results as $result) { - $stats[$metric]['data'][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; - $usage[$metric]['data'] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric]['data'][] = [ - 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, - 'date' => $formatDate, - ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'databasesTotal' => $usage[$metrics[0]]['total'], - 'collectionsTotal' => $usage[$metrics[1]]['total'], - 'documentsTotal' => $usage[$metrics[2]]['total'], - 'storageTotal' => $usage[$metrics[3]]['total'], - 'databasesReadsTotal' => $usage[$metrics[4]]['total'], - 'databasesWritesTotal' => $usage[$metrics[5]]['total'], - 'databases' => $usage[$metrics[0]]['data'], - 'collections' => $usage[$metrics[1]]['data'], - 'documents' => $usage[$metrics[2]]['data'], - 'storage' => $usage[$metrics[3]]['data'], - 'databasesReads' => $usage[$metrics[4]]['data'], - 'databasesWrites' => $usage[$metrics[5]]['data'], - ]), Response::MODEL_USAGE_DATABASES); - }); - -App::get('/v1/databases/:databaseId/usage') - ->desc('Get database usage stats') - ->groups(['api', 'database', 'usage']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: null, - name: 'getDatabaseUsage', - description: '/docs/references/databases/get-database-usage.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_USAGE_DATABASE, - ) - ], - contentType: ContentType::JSON, - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) { - - $database = $dbForProject->getDocument('databases', $databaseId); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_READS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_WRITES) - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', ['inf']) - ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', [$period]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric]['data'] = []; - foreach ($results as $result) { - $stats[$metric]['data'][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; - $usage[$metric]['data'] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric]['data'][] = [ - 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, - 'date' => $formatDate, - ]; - } - } - - $response->dynamic(new Document([ - 'range' => $range, - 'collectionsTotal' => $usage[$metrics[0]]['total'], - 'documentsTotal' => $usage[$metrics[1]]['total'], - 'storageTotal' => $usage[$metrics[2]]['total'], - 'databaseReadsTotal' => $usage[$metrics[3]]['total'], - 'databaseWritesTotal' => $usage[$metrics[4]]['total'], - 'collections' => $usage[$metrics[0]]['data'], - 'documents' => $usage[$metrics[1]]['data'], - 'storage' => $usage[$metrics[2]]['data'], - 'databaseReads' => $usage[$metrics[3]]['data'], - 'databaseWrites' => $usage[$metrics[4]]['data'], - ]), Response::MODEL_USAGE_DATABASE); - }); - -App::get('/v1/databases/:databaseId/tables/:tableId/usage') - ->alias('/v1/databases/:databaseId/collections/:tableId/usage') - ->desc('Get table usage stats') - ->groups(['api', 'database', 'usage']) - ->label('scope', 'collections.read') - ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('sdk', new Method( - namespace: 'databases', - group: null, - name: 'getTableUsage', - description: '/docs/references/databases/get-collection-usage.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_USAGE_COLLECTION, - ) - ], - contentType: ContentType::JSON, - )) - ->param('databaseId', '', new UID(), 'Database ID.') - ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) - ->param('tableId', '', new UID(), 'Collection ID.') - ->inject('response') - ->inject('dbForProject') - ->action(function (string $databaseId, string $range, string $tableId, Response $response, Database $dbForProject) { - - $database = $dbForProject->getDocument('databases', $databaseId); - $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); - - if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $tableDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', ['inf']) - ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', [$period]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric]['data'] = []; - foreach ($results as $result) { - $stats[$metric]['data'][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; - $usage[$metric]['data'] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric]['data'][] = [ - 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, - 'date' => $formatDate, - ]; - } - } - - $response->dynamic(new Document([ - 'range' => $range, - 'documentsTotal' => $usage[$metrics[0]]['total'], - 'documents' => $usage[$metrics[0]]['data'], - ]), Response::MODEL_USAGE_COLLECTION); - }); From ad16bd281e18c0a767c53560b181a4cf0e5933c8 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:16:08 +0530 Subject: [PATCH 020/173] add: database module to platform registry. --- src/Appwrite/Platform/Appwrite.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index ae3d4d6646..b2a92fcc00 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform; use Appwrite\Platform\Modules\Console; use Appwrite\Platform\Modules\Core; +use Appwrite\Platform\Modules\Databases; use Appwrite\Platform\Modules\Functions; use Appwrite\Platform\Modules\Projects; use Appwrite\Platform\Modules\Proxy; @@ -16,6 +17,7 @@ class Appwrite extends Platform public function __construct() { parent::__construct(new Core()); + $this->addModule(new Databases\Module()); $this->addModule(new Projects\Module()); $this->addModule(new Functions\Module()); $this->addModule(new Sites\Module()); From 6fac164d4646e5e51c872412218ea1a1f29826df Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:18:28 +0530 Subject: [PATCH 021/173] update: keep message same. --- .../Modules/Databases/Http/Columns/Relationship/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index 32cb235b62..09efdd14f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -128,7 +128,7 @@ class Create extends ColumnAction \strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() ) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique.'); + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); } if ( From 2554adb41e1eef549ee8abc1b5efd1a5127ca81b Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:29:11 +0530 Subject: [PATCH 022/173] update: error messages. --- .../Platform/Modules/Databases/Http/Columns/Float/Create.php | 2 +- .../Modules/Databases/Http/Columns/Relationship/Create.php | 2 +- .../Platform/Modules/Databases/Http/Databases/XList.php | 2 +- src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php | 2 +- src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index 0782f8eb29..b8f13d481b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -91,7 +91,7 @@ class Create extends ColumnAction $max ??= INF; if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum must be less than or equal to maximum'); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } $validator = new Range($min, $max, Database::VAR_FLOAT); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index 09efdd14f9..f5806b6d8c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -136,7 +136,7 @@ class Create extends ColumnAction $column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() ) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Only one "manyToMany" relationship per table is allowed.'); + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same table is currently not permitted.'); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php index 7453fb39f4..1fd0967b37 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php @@ -97,7 +97,7 @@ class XList extends Action $databases = $dbForProject->find('databases', $queries); $total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); } $response->dynamic(new Document([ 'databases' => $databases, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php index ab40f4d5e1..215ee94550 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -91,7 +91,7 @@ class Create extends Action } if (isset($data['$id'])) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new rows, try update instead'); } $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index d75150f2bb..85474840df 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -106,7 +106,7 @@ class XList extends Action $tables = $dbForProject->find('database_' . $database->getInternalId(), $queries); $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); } $response->dynamic(new Document([ From e0a4c698bc24505b508bd5e42f5d686b136f4607 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:43:32 +0530 Subject: [PATCH 023/173] fix: `float` column creation! --- .../Platform/Modules/Databases/Http/Columns/Float/Create.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index b8f13d481b..088eef0b39 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -87,8 +87,8 @@ class Create extends ColumnAction EventDatabase $queueForDatabase, Event $queueForEvents ): void { - $min ??= -INF; - $max ??= INF; + $min ??= -PHP_FLOAT_MAX; + $max ??= PHP_FLOAT_MAX; if ($min > $max) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); From d184d1c7b163f53764893e9d3857865b78417d38 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sun, 4 May 2025 18:50:24 +0530 Subject: [PATCH 024/173] fix: lint! --- .../Platform/Modules/Databases/Http/Columns/Float/Create.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index 088eef0b39..28e671fb72 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Float; -use const INF; - use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Extend\Exception; From 8504fbc0b93eb2802d8ae0fb1bfa68c97f61ddbe Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 09:14:20 +0530 Subject: [PATCH 025/173] update: `collection` response model to `table`. --- src/Appwrite/Utopia/Response.php | 12 ++++----- .../Utopia/Response/Model/BaseList.php | 6 ++--- .../Model/{Collection.php => Table.php} | 26 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) rename src/Appwrite/Utopia/Response/Model/{Collection.php => Table.php} (77%) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3d69ac1291..ad2e7ad9b8 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -31,7 +31,7 @@ use Appwrite\Utopia\Response\Model\AuthProvider; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Branch; use Appwrite\Utopia\Response\Model\Bucket; -use Appwrite\Utopia\Response\Model\Collection; +use Appwrite\Utopia\Response\Model\Table; use Appwrite\Utopia\Response\Model\ConsoleVariables; use Appwrite\Utopia\Response\Model\Continent; use Appwrite\Utopia\Response\Model\Country; @@ -160,8 +160,8 @@ class Response extends SwooleResponse // Database public const MODEL_DATABASE = 'database'; public const MODEL_DATABASE_LIST = 'databaseList'; - public const MODEL_COLLECTION = 'collection'; - public const MODEL_COLLECTION_LIST = 'collectionList'; + public const MODEL_TABLE = 'table'; + public const MODEL_TABLE_LIST = 'tableList'; public const MODEL_INDEX = 'index'; public const MODEL_INDEX_LIST = 'indexList'; public const MODEL_DOCUMENT = 'document'; @@ -376,8 +376,8 @@ class Response extends SwooleResponse ->setModel(new Error()) ->setModel(new ErrorDev()) // Lists - ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) - ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) + ->setModel(new BaseList('Rows List', self::MODEL_DOCUMENT_LIST, 'rows', self::MODEL_DOCUMENT)) + ->setModel(new BaseList('Tables List', self::MODEL_TABLE_LIST, 'tables', self::MODEL_TABLE)) ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) @@ -428,7 +428,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT)) // Entities ->setModel(new Database()) - ->setModel(new Collection()) + ->setModel(new Table()) ->setModel(new Attribute()) ->setModel(new AttributeList()) ->setModel(new AttributeString()) diff --git a/src/Appwrite/Utopia/Response/Model/BaseList.php b/src/Appwrite/Utopia/Response/Model/BaseList.php index d4ba6b0ab7..1fa3197ba7 100644 --- a/src/Appwrite/Utopia/Response/Model/BaseList.php +++ b/src/Appwrite/Utopia/Response/Model/BaseList.php @@ -32,15 +32,15 @@ class BaseList extends Model if ($paging) { $namesWithCap = [ - 'documents', 'collections', 'users', 'files', 'buckets', 'functions', + 'rows', 'tables', 'users', 'files', 'buckets', 'functions', 'deployments', 'executions', 'projects', 'webhooks', 'keys', 'platforms', 'rules', 'memberships', 'teams' ]; if (\in_array($name, $namesWithCap)) { - $description = 'Total number of ' . $key . ' documents that matched your query used as reference for offset pagination. When the `total` number of ' . $key . ' documents available is greater than 5000, total returned will be capped at 5000, and cursor pagination should be used. Read more about [pagination](https://appwrite.io/docs/pagination).'; + $description = 'Total number of ' . $key . ' rows that matched your query used as reference for offset pagination. When the `total` number of ' . $key . ' rows available is greater than 5000, total returned will be capped at 5000, and cursor pagination should be used. Read more about [pagination](https://appwrite.io/docs/pagination).'; } else { - $description = 'Total number of ' . $key . ' documents that matched your query.'; + $description = 'Total number of ' . $key . ' rows that matched your query.'; } $this->addRule('total', [ diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Table.php similarity index 77% rename from src/Appwrite/Utopia/Response/Model/Collection.php rename to src/Appwrite/Utopia/Response/Model/Table.php index 2c2bf0cca8..305f1db3c9 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -5,32 +5,32 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -class Collection extends Model +class Table extends Model { public function __construct() { $this ->addRule('$id', [ 'type' => self::TYPE_STRING, - 'description' => 'Collection ID.', + 'description' => 'Table ID.', 'default' => '', 'example' => '5e5ea5c16897e', ]) ->addRule('$createdAt', [ 'type' => self::TYPE_DATETIME, - 'description' => 'Collection creation date in ISO 8601 format.', + 'description' => 'Table creation date in ISO 8601 format.', 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]) ->addRule('$updatedAt', [ 'type' => self::TYPE_DATETIME, - 'description' => 'Collection update date in ISO 8601 format.', + 'description' => 'Table update date in ISO 8601 format.', 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]) ->addRule('$permissions', [ 'type' => self::TYPE_STRING, - 'description' => 'Collection permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', + 'description' => 'Table permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', 'default' => '', 'example' => ['read("any")'], 'array' => true @@ -43,13 +43,13 @@ class Collection extends Model ]) ->addRule('name', [ 'type' => self::TYPE_STRING, - 'description' => 'Collection name.', + 'description' => 'Table name.', 'default' => '', - 'example' => 'My Collection', + 'example' => 'My Table', ]) ->addRule('enabled', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Collection enabled. Can be \'enabled\' or \'disabled\'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys.', + 'description' => 'Table enabled. Can be \'enabled\' or \'disabled\'. When disabled, the table is inaccessible to users, but remains accessible to Server SDKs using API keys.', 'default' => true, 'example' => false, ]) @@ -59,7 +59,7 @@ class Collection extends Model 'default' => '', 'example' => true, ]) - ->addRule('attributes', [ + ->addRule('columns', [ 'type' => [ Response::MODEL_ATTRIBUTE_BOOLEAN, Response::MODEL_ATTRIBUTE_INTEGER, @@ -72,14 +72,14 @@ class Collection extends Model Response::MODEL_ATTRIBUTE_RELATIONSHIP, Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute ], - 'description' => 'Collection attributes.', + 'description' => 'Table columns.', 'default' => [], 'example' => new \stdClass(), 'array' => true, ]) ->addRule('indexes', [ 'type' => Response::MODEL_INDEX, - 'description' => 'Collection indexes.', + 'description' => 'Table indexes.', 'default' => [], 'example' => new \stdClass(), 'array' => true @@ -94,7 +94,7 @@ class Collection extends Model */ public function getName(): string { - return 'Collection'; + return 'Table'; } /** @@ -104,6 +104,6 @@ class Collection extends Model */ public function getType(): string { - return Response::MODEL_COLLECTION; + return Response::MODEL_TABLE; } } From 675d404b9b1c4bf9724a3bf4f715b9bb3544e296 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 09:30:27 +0530 Subject: [PATCH 026/173] update: `attribute*` response model to `column*`. --- app/config/events.php | 2 +- .../Databases/Http/Columns/Boolean/Create.php | 4 +- .../Databases/Http/Columns/Boolean/Update.php | 4 +- .../Http/Columns/Datetime/Create.php | 4 +- .../Http/Columns/Datetime/Update.php | 4 +- .../Modules/Databases/Http/Columns/Delete.php | 22 +++--- .../Databases/Http/Columns/Email/Create.php | 4 +- .../Databases/Http/Columns/Email/Update.php | 4 +- .../Databases/Http/Columns/Enum/Create.php | 4 +- .../Databases/Http/Columns/Enum/Update.php | 4 +- .../Databases/Http/Columns/Float/Create.php | 4 +- .../Databases/Http/Columns/Float/Update.php | 4 +- .../Modules/Databases/Http/Columns/Get.php | 42 +++++----- .../Databases/Http/Columns/IP/Create.php | 4 +- .../Databases/Http/Columns/IP/Update.php | 4 +- .../Databases/Http/Columns/Integer/Create.php | 4 +- .../Databases/Http/Columns/Integer/Update.php | 4 +- .../Http/Columns/Relationship/Create.php | 4 +- .../Http/Columns/Relationship/Update.php | 4 +- .../Databases/Http/Columns/String/Create.php | 4 +- .../Databases/Http/Columns/String/Update.php | 4 +- .../Databases/Http/Columns/URL/Create.php | 4 +- .../Databases/Http/Columns/URL/Update.php | 4 +- .../Modules/Databases/Http/Columns/XList.php | 4 +- src/Appwrite/Utopia/Response.php | 78 +++++++++---------- .../Utopia/Response/Model/AttributeList.php | 58 -------------- .../Model/{Attribute.php => Column.php} | 22 +++--- ...AttributeBoolean.php => ColumnBoolean.php} | 10 +-- ...tributeDatetime.php => ColumnDatetime.php} | 10 +-- .../{AttributeEmail.php => ColumnEmail.php} | 10 +-- .../{AttributeEnum.php => ColumnEnum.php} | 10 +-- .../{AttributeFloat.php => ColumnFloat.php} | 10 +-- .../Model/{AttributeIP.php => ColumnIP.php} | 10 +-- ...AttributeInteger.php => ColumnInteger.php} | 10 +-- .../Utopia/Response/Model/ColumnList.php | 58 ++++++++++++++ ...elationship.php => ColumnRelationship.php} | 10 +-- .../{AttributeString.php => ColumnString.php} | 10 +-- .../Model/{AttributeURL.php => ColumnURL.php} | 14 ++-- src/Appwrite/Utopia/Response/Model/Table.php | 20 ++--- 39 files changed, 245 insertions(+), 245 deletions(-) delete mode 100644 src/Appwrite/Utopia/Response/Model/AttributeList.php rename src/Appwrite/Utopia/Response/Model/{Attribute.php => Column.php} (74%) rename src/Appwrite/Utopia/Response/Model/{AttributeBoolean.php => ColumnBoolean.php} (83%) rename src/Appwrite/Utopia/Response/Model/{AttributeDatetime.php => ColumnDatetime.php} (86%) rename src/Appwrite/Utopia/Response/Model/{AttributeEmail.php => ColumnEmail.php} (86%) rename src/Appwrite/Utopia/Response/Model/{AttributeEnum.php => ColumnEnum.php} (88%) rename src/Appwrite/Utopia/Response/Model/{AttributeFloat.php => ColumnFloat.php} (88%) rename src/Appwrite/Utopia/Response/Model/{AttributeIP.php => ColumnIP.php} (87%) rename src/Appwrite/Utopia/Response/Model/{AttributeInteger.php => ColumnInteger.php} (88%) create mode 100644 src/Appwrite/Utopia/Response/Model/ColumnList.php rename src/Appwrite/Utopia/Response/Model/{AttributeRelationship.php => ColumnRelationship.php} (91%) rename src/Appwrite/Utopia/Response/Model/{AttributeString.php => ColumnString.php} (74%) rename src/Appwrite/Utopia/Response/Model/{AttributeURL.php => ColumnURL.php} (76%) diff --git a/app/config/events.php b/app/config/events.php index 3b0cc982a0..41857fad70 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -125,7 +125,7 @@ return [ ] ], 'columns' => [ - '$model' => Response::MODEL_ATTRIBUTE, + '$model' => Response::MODEL_COLUMN, '$resource' => true, '$description' => 'This event triggers on any columns event.', 'create' => [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php index dab0527fa7..7e1331a637 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php @@ -48,7 +48,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + model: UtopiaResponse::MODEL_COLUMN_BOOLEAN, ) ] )) @@ -79,6 +79,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_BOOLEAN); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php index 35148fc174..6725e0c817 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php @@ -48,7 +48,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + model: UtopiaResponse::MODEL_COLUMN_BOOLEAN, ) ], contentType: ContentType::JSON @@ -81,6 +81,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_BOOLEAN); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php index 36ddfb4dfa..775d33ab19 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php @@ -50,7 +50,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + model: UtopiaResponse::MODEL_COLUMN_DATETIME, ) ] )) @@ -101,6 +101,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_DATETIME); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php index d323ed4fd0..6ff0744859 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php @@ -50,7 +50,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + model: UtopiaResponse::MODEL_COLUMN_DATETIME, ) ], contentType: ContentType::JSON @@ -92,6 +92,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_DATETIME); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php index 9baa857fec..945097e66b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php @@ -136,19 +136,19 @@ class Delete extends Action $format = $column->getAttribute('format'); $model = match ($type) { - Database::VAR_BOOLEAN => UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, - Database::VAR_INTEGER => UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, - Database::VAR_FLOAT => UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, - Database::VAR_DATETIME => UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, - Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, + Database::VAR_BOOLEAN => UtopiaResponse::MODEL_COLUMN_BOOLEAN, + Database::VAR_INTEGER => UtopiaResponse::MODEL_COLUMN_INTEGER, + Database::VAR_FLOAT => UtopiaResponse::MODEL_COLUMN_FLOAT, + Database::VAR_DATETIME => UtopiaResponse::MODEL_COLUMN_DATETIME, + Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, Database::VAR_STRING => match ($format) { - APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, - APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_ATTRIBUTE_ENUM, - APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_ATTRIBUTE_IP, - APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_ATTRIBUTE_URL, - default => UtopiaResponse::MODEL_ATTRIBUTE_STRING, + APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_COLUMN_EMAIL, + APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_COLUMN_ENUM, + APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_COLUMN_IP, + APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_COLUMN_URL, + default => UtopiaResponse::MODEL_COLUMN_STRING, }, - default => UtopiaResponse::MODEL_ATTRIBUTE, + default => UtopiaResponse::MODEL_COLUMN, }; $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php index 1d51e26efc..2372d8b032 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php @@ -50,7 +50,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + model: UtopiaResponse::MODEL_COLUMN_EMAIL, ) ] )) @@ -99,6 +99,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_EMAIL); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php index 53e03c93a2..74124eba37 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php @@ -50,7 +50,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + model: UtopiaResponse::MODEL_COLUMN_EMAIL, ) ], contentType: ContentType::JSON @@ -93,6 +93,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_EMAIL); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php index 95ed0b0630..5c5c7da071 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php @@ -52,7 +52,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + model: UtopiaResponse::MODEL_COLUMN_ENUM, ) ] )) @@ -108,6 +108,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_ENUM); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_ENUM); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php index d52dd161e4..aa588a9f1b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php @@ -51,7 +51,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + model: UtopiaResponse::MODEL_COLUMN_ENUM, ) ], contentType: ContentType::JSON @@ -97,6 +97,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_ENUM); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_ENUM); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index 28e671fb72..150ce8d25e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -52,7 +52,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + model: UtopiaResponse::MODEL_COLUMN_FLOAT, ) ] )) @@ -116,6 +116,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_FLOAT); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php index c585205388..8ea7ab474a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php @@ -50,7 +50,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + model: UtopiaResponse::MODEL_COLUMN_FLOAT, ) ], contentType: ContentType::JSON @@ -104,6 +104,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_FLOAT); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php index 14744f0442..160ab60868 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php @@ -44,16 +44,16 @@ class Get extends Action new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, model: [ - UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, - UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, - UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, - UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, - UtopiaResponse::MODEL_ATTRIBUTE_ENUM, - UtopiaResponse::MODEL_ATTRIBUTE_URL, - UtopiaResponse::MODEL_ATTRIBUTE_IP, - UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, - UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, - UtopiaResponse::MODEL_ATTRIBUTE_STRING, + UtopiaResponse::MODEL_COLUMN_BOOLEAN, + UtopiaResponse::MODEL_COLUMN_INTEGER, + UtopiaResponse::MODEL_COLUMN_FLOAT, + UtopiaResponse::MODEL_COLUMN_EMAIL, + UtopiaResponse::MODEL_COLUMN_ENUM, + UtopiaResponse::MODEL_COLUMN_URL, + UtopiaResponse::MODEL_COLUMN_IP, + UtopiaResponse::MODEL_COLUMN_DATETIME, + UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, + UtopiaResponse::MODEL_COLUMN_STRING, ] ) ] @@ -92,19 +92,19 @@ class Get extends Action } $model = match ($type) { - Database::VAR_BOOLEAN => UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, - Database::VAR_INTEGER => UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, - Database::VAR_FLOAT => UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, - Database::VAR_DATETIME => UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, - Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, + Database::VAR_BOOLEAN => UtopiaResponse::MODEL_COLUMN_BOOLEAN, + Database::VAR_INTEGER => UtopiaResponse::MODEL_COLUMN_INTEGER, + Database::VAR_FLOAT => UtopiaResponse::MODEL_COLUMN_FLOAT, + Database::VAR_DATETIME => UtopiaResponse::MODEL_COLUMN_DATETIME, + Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, Database::VAR_STRING => match ($format) { - APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, - APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_ATTRIBUTE_ENUM, - APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_ATTRIBUTE_IP, - APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_ATTRIBUTE_URL, - default => UtopiaResponse::MODEL_ATTRIBUTE_STRING, + APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_COLUMN_EMAIL, + APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_COLUMN_ENUM, + APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_COLUMN_IP, + APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_COLUMN_URL, + default => UtopiaResponse::MODEL_COLUMN_STRING, }, - default => UtopiaResponse::MODEL_ATTRIBUTE, + default => UtopiaResponse::MODEL_COLUMN, }; $response->dynamic($column, $model); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php index b3916a5fad..73d8c93031 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php @@ -50,7 +50,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_IP, + model: UtopiaResponse::MODEL_COLUMN_IP, ) ] )) @@ -91,6 +91,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_IP); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_IP); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php index 6da08a2c9f..4b9fa8bca8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php @@ -50,7 +50,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_IP, + model: UtopiaResponse::MODEL_COLUMN_IP, ) ], contentType: ContentType::JSON @@ -93,6 +93,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_IP); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_IP); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php index 7a26d10bc8..d58b79d597 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php @@ -52,7 +52,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + model: UtopiaResponse::MODEL_COLUMN_INTEGER, ) ] )) @@ -118,6 +118,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_INTEGER); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php index 27616c07c9..1806cc91c3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php @@ -50,7 +50,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + model: UtopiaResponse::MODEL_COLUMN_INTEGER, ) ], contentType: ContentType::JSON @@ -104,6 +104,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_INTEGER); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index f5806b6d8c..d063c8e055 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -52,7 +52,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP + model: UtopiaResponse::MODEL_COLUMN_RELATIONSHIP ) ] )) @@ -163,6 +163,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php index 34231ad611..80ca25e9cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php @@ -48,7 +48,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP + model: UtopiaResponse::MODEL_COLUMN_RELATIONSHIP ) ], contentType: ContentType::JSON @@ -98,6 +98,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php index 1987f5fdbe..caafdbc32f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php @@ -53,7 +53,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_STRING + model: UtopiaResponse::MODEL_COLUMN_STRING ) ] )) @@ -117,6 +117,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_STRING); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_STRING); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php index a73ae0f9b1..35fe6bc86d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php @@ -51,7 +51,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_STRING, + model: UtopiaResponse::MODEL_COLUMN_STRING, ) ], contentType: ContentType::JSON @@ -96,6 +96,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_STRING); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_STRING); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php index 74eedfa98b..c1483c373a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php @@ -50,7 +50,7 @@ class Create extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_ATTRIBUTE_URL, + model: UtopiaResponse::MODEL_COLUMN_URL, ) ] )) @@ -91,6 +91,6 @@ class Create extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_URL); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_URL); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php index 109b9c575b..db35cf56c4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php @@ -50,7 +50,7 @@ class Update extends ColumnAction responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_URL, + model: UtopiaResponse::MODEL_COLUMN_URL, ) ], contentType: ContentType::JSON @@ -93,6 +93,6 @@ class Update extends ColumnAction $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_URL); + ->dynamic($column, UtopiaResponse::MODEL_COLUMN_URL); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php index f2343e927b..cba6e2d077 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -47,7 +47,7 @@ class XList extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ATTRIBUTE_LIST + model: UtopiaResponse::MODEL_COLUMN_LIST ) ] )) @@ -120,6 +120,6 @@ class XList extends Action $response->dynamic(new Document([ 'attributes' => $columns, 'total' => $total, - ]), UtopiaResponse::MODEL_ATTRIBUTE_LIST); + ]), UtopiaResponse::MODEL_COLUMN_LIST); } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index ad2e7ad9b8..0fae030b82 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -15,23 +15,22 @@ use Appwrite\Utopia\Response\Model\AlgoScrypt; use Appwrite\Utopia\Response\Model\AlgoScryptModified; use Appwrite\Utopia\Response\Model\AlgoSha; use Appwrite\Utopia\Response\Model\Any; -use Appwrite\Utopia\Response\Model\Attribute; -use Appwrite\Utopia\Response\Model\AttributeBoolean; -use Appwrite\Utopia\Response\Model\AttributeDatetime; -use Appwrite\Utopia\Response\Model\AttributeEmail; -use Appwrite\Utopia\Response\Model\AttributeEnum; -use Appwrite\Utopia\Response\Model\AttributeFloat; -use Appwrite\Utopia\Response\Model\AttributeInteger; -use Appwrite\Utopia\Response\Model\AttributeIP; -use Appwrite\Utopia\Response\Model\AttributeList; -use Appwrite\Utopia\Response\Model\AttributeRelationship; -use Appwrite\Utopia\Response\Model\AttributeString; -use Appwrite\Utopia\Response\Model\AttributeURL; use Appwrite\Utopia\Response\Model\AuthProvider; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Branch; use Appwrite\Utopia\Response\Model\Bucket; -use Appwrite\Utopia\Response\Model\Table; +use Appwrite\Utopia\Response\Model\Column; +use Appwrite\Utopia\Response\Model\ColumnBoolean; +use Appwrite\Utopia\Response\Model\ColumnDatetime; +use Appwrite\Utopia\Response\Model\ColumnEmail; +use Appwrite\Utopia\Response\Model\ColumnEnum; +use Appwrite\Utopia\Response\Model\ColumnFloat; +use Appwrite\Utopia\Response\Model\ColumnInteger; +use Appwrite\Utopia\Response\Model\ColumnIP; +use Appwrite\Utopia\Response\Model\ColumnList; +use Appwrite\Utopia\Response\Model\ColumnRelationship; +use Appwrite\Utopia\Response\Model\ColumnString; +use Appwrite\Utopia\Response\Model\ColumnURL; use Appwrite\Utopia\Response\Model\ConsoleVariables; use Appwrite\Utopia\Response\Model\Continent; use Appwrite\Utopia\Response\Model\Country; @@ -94,6 +93,7 @@ use Appwrite\Utopia\Response\Model\Session; use Appwrite\Utopia\Response\Model\Site; use Appwrite\Utopia\Response\Model\Specification; use Appwrite\Utopia\Response\Model\Subscriber; +use Appwrite\Utopia\Response\Model\Table; use Appwrite\Utopia\Response\Model\Target; use Appwrite\Utopia\Response\Model\Team; use Appwrite\Utopia\Response\Model\TemplateEmail; @@ -168,18 +168,18 @@ class Response extends SwooleResponse public const MODEL_DOCUMENT_LIST = 'documentList'; // Database Attributes - public const MODEL_ATTRIBUTE = 'attribute'; - public const MODEL_ATTRIBUTE_LIST = 'attributeList'; - public const MODEL_ATTRIBUTE_STRING = 'attributeString'; - public const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger'; - public const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat'; - public const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean'; - public const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail'; - public const MODEL_ATTRIBUTE_ENUM = 'attributeEnum'; - public const MODEL_ATTRIBUTE_IP = 'attributeIp'; - public const MODEL_ATTRIBUTE_URL = 'attributeUrl'; - public const MODEL_ATTRIBUTE_DATETIME = 'attributeDatetime'; - public const MODEL_ATTRIBUTE_RELATIONSHIP = 'attributeRelationship'; + public const MODEL_COLUMN = 'column'; + public const MODEL_COLUMN_LIST = 'columnList'; + public const MODEL_COLUMN_STRING = 'columnString'; + public const MODEL_COLUMN_INTEGER = 'columnInteger'; + public const MODEL_COLUMN_FLOAT = 'columnFloat'; + public const MODEL_COLUMN_BOOLEAN = 'columnBoolean'; + public const MODEL_COLUMN_EMAIL = 'columnEmail'; + public const MODEL_COLUMN_ENUM = 'columnEnum'; + public const MODEL_COLUMN_IP = 'columnIp'; + public const MODEL_COLUMN_URL = 'columnUrl'; + public const MODEL_COLUMN_DATETIME = 'columnDatetime'; + public const MODEL_COLUMN_RELATIONSHIP = 'columnRelationship'; // Users public const MODEL_ACCOUNT = 'account'; @@ -429,18 +429,18 @@ class Response extends SwooleResponse // Entities ->setModel(new Database()) ->setModel(new Table()) - ->setModel(new Attribute()) - ->setModel(new AttributeList()) - ->setModel(new AttributeString()) - ->setModel(new AttributeInteger()) - ->setModel(new AttributeFloat()) - ->setModel(new AttributeBoolean()) - ->setModel(new AttributeEmail()) - ->setModel(new AttributeEnum()) - ->setModel(new AttributeIP()) - ->setModel(new AttributeURL()) - ->setModel(new AttributeDatetime()) - ->setModel(new AttributeRelationship()) + ->setModel(new Column()) + ->setModel(new ColumnList()) + ->setModel(new ColumnString()) + ->setModel(new ColumnInteger()) + ->setModel(new ColumnFloat()) + ->setModel(new ColumnBoolean()) + ->setModel(new ColumnEmail()) + ->setModel(new ColumnEnum()) + ->setModel(new ColumnIP()) + ->setModel(new ColumnURL()) + ->setModel(new ColumnDatetime()) + ->setModel(new ColumnRelationship()) ->setModel(new Index()) ->setModel(new ModelDocument()) ->setModel(new Log()) @@ -558,7 +558,7 @@ class Response extends SwooleResponse * * @return self */ - public function setModel(Model $instance) + public function setModel(Model $instance): Response { $this->models[$instance->getType()] = $instance; @@ -836,7 +836,7 @@ class Response extends SwooleResponse /** * Function to add a response filter, the order of filters are first in - first out. * - * @param $filter the response filter to set + * @param $filter - the response filter to set * * @return void */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php deleted file mode 100644 index 8b04475a14..0000000000 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ /dev/null @@ -1,58 +0,0 @@ -addRule('total', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'Total number of attributes in the given collection.', - 'default' => 0, - 'example' => 5, - ]) - ->addRule('attributes', [ - 'type' => [ - Response::MODEL_ATTRIBUTE_BOOLEAN, - Response::MODEL_ATTRIBUTE_INTEGER, - Response::MODEL_ATTRIBUTE_FLOAT, - Response::MODEL_ATTRIBUTE_EMAIL, - Response::MODEL_ATTRIBUTE_ENUM, - Response::MODEL_ATTRIBUTE_URL, - Response::MODEL_ATTRIBUTE_IP, - Response::MODEL_ATTRIBUTE_DATETIME, - Response::MODEL_ATTRIBUTE_RELATIONSHIP, - Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute - ], - 'description' => 'List of attributes.', - 'default' => [], - 'array' => true - ]) - ; - } - - /** - * Get Name - * - * @return string - */ - public function getName(): string - { - return 'Attributes List'; - } - - /** - * Get Type - * - * @return string - */ - public function getType(): string - { - return Response::MODEL_ATTRIBUTE_LIST; - } -} diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Column.php similarity index 74% rename from src/Appwrite/Utopia/Response/Model/Attribute.php rename to src/Appwrite/Utopia/Response/Model/Column.php index 8c43f8d21c..5562de39f2 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Column.php @@ -5,57 +5,57 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -class Attribute extends Model +class Column extends Model { public function __construct() { $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'fullName', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'string', ]) ->addRule('status', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', + 'description' => 'Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', 'default' => '', 'example' => 'available', ]) ->addRule('error', [ 'type' => self::TYPE_STRING, - 'description' => 'Error message. Displays error generated on failure of creating or deleting an attribute.', + 'description' => 'Error message. Displays error generated on failure of creating or deleting an column.', 'default' => '', 'example' => 'string', ]) ->addRule('required', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', + 'description' => 'Is column required?', 'default' => false, 'example' => true, ]) ->addRule('array', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', + 'description' => 'Is column an array?', 'default' => false, 'required' => false, 'example' => false, ]) ->addRule('$createdAt', [ 'type' => self::TYPE_DATETIME, - 'description' => 'Attribute creation date in ISO 8601 format.', + 'description' => 'Column creation date in ISO 8601 format.', 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]) ->addRule('$updatedAt', [ 'type' => self::TYPE_DATETIME, - 'description' => 'Attribute update date in ISO 8601 format.', + 'description' => 'Column update date in ISO 8601 format.', 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]); @@ -70,7 +70,7 @@ class Attribute extends Model */ public function getName(): string { - return 'Attribute'; + return 'Column'; } /** @@ -80,6 +80,6 @@ class Attribute extends Model */ public function getType(): string { - return Response::MODEL_ATTRIBUTE; + return Response::MODEL_COLUMN; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/ColumnBoolean.php similarity index 83% rename from src/Appwrite/Utopia/Response/Model/AttributeBoolean.php rename to src/Appwrite/Utopia/Response/Model/ColumnBoolean.php index 05846817ca..ffbfe0e793 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnBoolean.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeBoolean extends Attribute +class ColumnBoolean extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeBoolean extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'isEnabled', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'boolean', ]) @@ -44,7 +44,7 @@ class AttributeBoolean extends Attribute */ public function getName(): string { - return 'AttributeBoolean'; + return 'ColumnBoolean'; } /** @@ -54,6 +54,6 @@ class AttributeBoolean extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_BOOLEAN; + return Response::MODEL_COLUMN_BOOLEAN; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeDatetime.php b/src/Appwrite/Utopia/Response/Model/ColumnDatetime.php similarity index 86% rename from src/Appwrite/Utopia/Response/Model/AttributeDatetime.php rename to src/Appwrite/Utopia/Response/Model/ColumnDatetime.php index 4651aebd06..4c68102775 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeDatetime.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnDatetime.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeDatetime extends Attribute +class ColumnDatetime extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeDatetime extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'birthDay', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => self::TYPE_DATETIME, ]) @@ -52,7 +52,7 @@ class AttributeDatetime extends Attribute */ public function getName(): string { - return 'AttributeDatetime'; + return 'ColumnDatetime'; } /** @@ -62,6 +62,6 @@ class AttributeDatetime extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_DATETIME; + return Response::MODEL_COLUMN_DATETIME; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/ColumnEmail.php similarity index 86% rename from src/Appwrite/Utopia/Response/Model/AttributeEmail.php rename to src/Appwrite/Utopia/Response/Model/ColumnEmail.php index 078087dd4b..446f993227 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnEmail.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeEmail extends Attribute +class ColumnEmail extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeEmail extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'userEmail', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'string', ]) @@ -51,7 +51,7 @@ class AttributeEmail extends Attribute */ public function getName(): string { - return 'AttributeEmail'; + return 'ColumnEmail'; } /** @@ -61,6 +61,6 @@ class AttributeEmail extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_EMAIL; + return Response::MODEL_COLUMN_EMAIL; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEnum.php b/src/Appwrite/Utopia/Response/Model/ColumnEnum.php similarity index 88% rename from src/Appwrite/Utopia/Response/Model/AttributeEnum.php rename to src/Appwrite/Utopia/Response/Model/ColumnEnum.php index 992b62ee3a..b0873a2f7b 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEnum.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnEnum.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeEnum extends Attribute +class ColumnEnum extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeEnum extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'status', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'string', ]) @@ -58,7 +58,7 @@ class AttributeEnum extends Attribute */ public function getName(): string { - return 'AttributeEnum'; + return 'ColumnEnum'; } /** @@ -68,6 +68,6 @@ class AttributeEnum extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_ENUM; + return Response::MODEL_COLUMN_ENUM; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/ColumnFloat.php similarity index 88% rename from src/Appwrite/Utopia/Response/Model/AttributeFloat.php rename to src/Appwrite/Utopia/Response/Model/ColumnFloat.php index 266b89c330..5867ac8712 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnFloat.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeFloat extends Attribute +class ColumnFloat extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeFloat extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'percentageCompleted', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'double', ]) @@ -58,7 +58,7 @@ class AttributeFloat extends Attribute */ public function getName(): string { - return 'AttributeFloat'; + return 'ColumnFloat'; } /** @@ -68,6 +68,6 @@ class AttributeFloat extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_FLOAT; + return Response::MODEL_COLUMN_FLOAT; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/ColumnIP.php similarity index 87% rename from src/Appwrite/Utopia/Response/Model/AttributeIP.php rename to src/Appwrite/Utopia/Response/Model/ColumnIP.php index cfa309317a..2ca7130720 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnIP.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeIP extends Attribute +class ColumnIP extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeIP extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'ipAddress', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'string', ]) @@ -51,7 +51,7 @@ class AttributeIP extends Attribute */ public function getName(): string { - return 'AttributeIP'; + return 'ColumnIP'; } /** @@ -61,6 +61,6 @@ class AttributeIP extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_IP; + return Response::MODEL_COLUMN_IP; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/ColumnInteger.php similarity index 88% rename from src/Appwrite/Utopia/Response/Model/AttributeInteger.php rename to src/Appwrite/Utopia/Response/Model/ColumnInteger.php index fddfe57445..086f36a961 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnInteger.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeInteger extends Attribute +class ColumnInteger extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeInteger extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'count', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'integer', ]) @@ -57,7 +57,7 @@ class AttributeInteger extends Attribute */ public function getName(): string { - return 'AttributeInteger'; + return 'ColumnInteger'; } /** @@ -67,6 +67,6 @@ class AttributeInteger extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_INTEGER; + return Response::MODEL_COLUMN_INTEGER; } } diff --git a/src/Appwrite/Utopia/Response/Model/ColumnList.php b/src/Appwrite/Utopia/Response/Model/ColumnList.php new file mode 100644 index 0000000000..0c9f5a49b5 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ColumnList.php @@ -0,0 +1,58 @@ +addRule('total', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total number of columns in the given table.', + 'default' => 0, + 'example' => 5, + ]) + ->addRule('columns', [ + 'type' => [ + Response::MODEL_COLUMN_BOOLEAN, + Response::MODEL_COLUMN_INTEGER, + Response::MODEL_COLUMN_FLOAT, + Response::MODEL_COLUMN_EMAIL, + Response::MODEL_COLUMN_ENUM, + Response::MODEL_COLUMN_URL, + Response::MODEL_COLUMN_IP, + Response::MODEL_COLUMN_DATETIME, + Response::MODEL_COLUMN_RELATIONSHIP, + Response::MODEL_COLUMN_STRING // needs to be last, since its condition would dominate any other string attribute + ], + 'description' => 'List of columns.', + 'default' => [], + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'Columns List'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_COLUMN_LIST; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php similarity index 91% rename from src/Appwrite/Utopia/Response/Model/AttributeRelationship.php rename to src/Appwrite/Utopia/Response/Model/ColumnRelationship.php index d88fbd1530..2c5649b5bf 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Utopia\Database\Document; -class AttributeRelationship extends Attribute +class ColumnRelationship extends Column { public function __construct() { @@ -14,9 +14,9 @@ class AttributeRelationship extends Attribute $this ->addRule('relatedCollection', [ 'type' => self::TYPE_STRING, - 'description' => 'The ID of the related collection.', + 'description' => 'The ID of the related table.', 'default' => null, - 'example' => 'collection', + 'example' => 'table', ]) ->addRule('relationType', [ 'type' => self::TYPE_STRING, @@ -62,7 +62,7 @@ class AttributeRelationship extends Attribute */ public function getName(): string { - return 'AttributeRelationship'; + return 'ColumnRelationship'; } /** @@ -72,7 +72,7 @@ class AttributeRelationship extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_RELATIONSHIP; + return Response::MODEL_COLUMN_RELATIONSHIP; } /** diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/ColumnString.php similarity index 74% rename from src/Appwrite/Utopia/Response/Model/AttributeString.php rename to src/Appwrite/Utopia/Response/Model/ColumnString.php index 12bb42009d..f962e51c37 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnString.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeString extends Attribute +class ColumnString extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeString extends Attribute $this ->addRule('size', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Attribute size.', + 'description' => 'Column size.', 'default' => 0, 'example' => 128, ]) ->addRule('default', [ 'type' => self::TYPE_STRING, - 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'description' => 'Default value for column when not provided. Cannot be set when column is required.', 'default' => null, 'required' => false, 'example' => 'default', @@ -38,7 +38,7 @@ class AttributeString extends Attribute */ public function getName(): string { - return 'AttributeString'; + return 'ColumnString'; } /** @@ -48,6 +48,6 @@ class AttributeString extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_STRING; + return Response::MODEL_COLUMN_STRING; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/ColumnURL.php similarity index 76% rename from src/Appwrite/Utopia/Response/Model/AttributeURL.php rename to src/Appwrite/Utopia/Response/Model/ColumnURL.php index 633d5b49d7..12850b3907 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnURL.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -class AttributeURL extends Attribute +class ColumnURL extends Column { public function __construct() { @@ -13,13 +13,13 @@ class AttributeURL extends Attribute $this ->addRule('key', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', + 'description' => 'Column Key.', 'default' => '', 'example' => 'githubUrl', ]) ->addRule('type', [ 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', + 'description' => 'Column type.', 'default' => '', 'example' => 'string', ]) @@ -31,10 +31,10 @@ class AttributeURL extends Attribute ]) ->addRule('default', [ 'type' => self::TYPE_STRING, - 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'description' => 'Default value for column when not provided. Cannot be set when column is required.', 'default' => null, 'required' => false, - 'example' => 'http://example.com', + 'example' => 'https://example.com', ]) ; } @@ -51,7 +51,7 @@ class AttributeURL extends Attribute */ public function getName(): string { - return 'AttributeURL'; + return 'ColumnURL'; } /** @@ -61,6 +61,6 @@ class AttributeURL extends Attribute */ public function getType(): string { - return Response::MODEL_ATTRIBUTE_URL; + return Response::MODEL_COLUMN_URL; } } diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index 305f1db3c9..d7098f313f 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -61,16 +61,16 @@ class Table extends Model ]) ->addRule('columns', [ 'type' => [ - Response::MODEL_ATTRIBUTE_BOOLEAN, - Response::MODEL_ATTRIBUTE_INTEGER, - Response::MODEL_ATTRIBUTE_FLOAT, - Response::MODEL_ATTRIBUTE_EMAIL, - Response::MODEL_ATTRIBUTE_ENUM, - Response::MODEL_ATTRIBUTE_URL, - Response::MODEL_ATTRIBUTE_IP, - Response::MODEL_ATTRIBUTE_DATETIME, - Response::MODEL_ATTRIBUTE_RELATIONSHIP, - Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute + Response::MODEL_COLUMN_BOOLEAN, + Response::MODEL_COLUMN_INTEGER, + Response::MODEL_COLUMN_FLOAT, + Response::MODEL_COLUMN_EMAIL, + Response::MODEL_COLUMN_ENUM, + Response::MODEL_COLUMN_URL, + Response::MODEL_COLUMN_IP, + Response::MODEL_COLUMN_DATETIME, + Response::MODEL_COLUMN_RELATIONSHIP, + Response::MODEL_COLUMN_STRING, // needs to be last, since its condition would dominate any other string attribute ], 'description' => 'Table columns.', 'default' => [], From 7a5368078622fdaa8ef08ffb55121586dda197e1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 09:42:33 +0530 Subject: [PATCH 027/173] update: `document` response model to `row`; refactor: old collection to table models on response types. --- app/config/events.php | 4 +-- .../Modules/Databases/Http/Rows/Create.php | 4 +-- .../Modules/Databases/Http/Rows/Delete.php | 2 +- .../Modules/Databases/Http/Rows/Get.php | 4 +-- .../Modules/Databases/Http/Rows/Update.php | 4 +-- .../Modules/Databases/Http/Rows/XList.php | 4 +-- .../Modules/Databases/Http/Tables/Create.php | 4 +-- .../Modules/Databases/Http/Tables/Delete.php | 2 +- .../Modules/Databases/Http/Tables/Get.php | 4 +-- .../Modules/Databases/Http/Tables/Update.php | 4 +-- .../Modules/Databases/Http/Tables/XList.php | 6 ++--- src/Appwrite/Utopia/Response.php | 10 +++---- .../Response/Model/{Document.php => Row.php} | 26 +++++++++---------- tests/unit/GraphQL/BuilderTest.php | 4 +-- 14 files changed, 41 insertions(+), 41 deletions(-) rename src/Appwrite/Utopia/Response/Model/{Document.php => Row.php} (73%) diff --git a/app/config/events.php b/app/config/events.php index 41857fad70..4523b56206 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -96,11 +96,11 @@ return [ '$resource' => true, '$description' => 'This event triggers on any database event.', 'tables' => [ - '$model' => Response::MODEL_COLLECTION, + '$model' => Response::MODEL_TABLE, '$resource' => true, '$description' => 'This event triggers on any table event.', 'rows' => [ - '$model' => Response::MODEL_DOCUMENT, + '$model' => Response::MODEL_ROW, '$resource' => true, '$description' => 'This event triggers on any rows event.', 'create' => [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php index 215ee94550..55efbc3632 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -63,7 +63,7 @@ class Create extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, - model: UtopiaResponse::MODEL_DOCUMENT, + model: UtopiaResponse::MODEL_ROW, ) ], contentType: ContentType::JSON @@ -293,7 +293,7 @@ class Create extends Action $response ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($row, UtopiaResponse::MODEL_DOCUMENT); + ->dynamic($row, UtopiaResponse::MODEL_ROW); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php index 61ed961c64..bba042bca5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php @@ -160,7 +160,7 @@ class Delete extends Action ->setParam('rowId', $row->getId()) ->setContext('table', $table) ->setContext('database', $database) - ->setPayload($response->output($row, UtopiaResponse::MODEL_DOCUMENT), sensitive: $relationships); + ->setPayload($response->output($row, UtopiaResponse::MODEL_ROW), sensitive: $relationships); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php index a3a484be70..85b6127bd3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -51,7 +51,7 @@ class Get extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_DOCUMENT, + model: UtopiaResponse::MODEL_ROW, ) ], contentType: ContentType::JSON @@ -149,6 +149,6 @@ class Get extends Action $response->addHeader('X-Debug-Operations', $operations); - $response->dynamic($row, UtopiaResponse::MODEL_DOCUMENT); + $response->dynamic($row, UtopiaResponse::MODEL_ROW); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index bc41118206..b7f3a4f6c4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -62,7 +62,7 @@ class Update extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_DOCUMENT, + model: UtopiaResponse::MODEL_ROW, ) ], contentType: ContentType::JSON @@ -281,7 +281,7 @@ class Update extends Action $processRow($table, $row); - $response->dynamic($row, UtopiaResponse::MODEL_DOCUMENT); + $response->dynamic($row, UtopiaResponse::MODEL_ROW); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php index b27930405f..e93d72cad6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -52,7 +52,7 @@ class XList extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_DOCUMENT_LIST, + model: UtopiaResponse::MODEL_ROW_LIST, ) ], contentType: ContentType::JSON @@ -218,6 +218,6 @@ class XList extends Action $response->dynamic(new Document([ 'total' => $total, 'documents' => $rows, - ]), UtopiaResponse::MODEL_DOCUMENT_LIST); + ]), UtopiaResponse::MODEL_ROW_LIST); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php index ead5a64f88..9acd248190 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php @@ -56,7 +56,7 @@ class Create extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, - model: UtopiaResponse::MODEL_COLLECTION, + model: UtopiaResponse::MODEL_TABLE, ) ], contentType: ContentType::JSON @@ -111,6 +111,6 @@ class Create extends Action $response ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($table, UtopiaResponse::MODEL_COLLECTION); + ->dynamic($table, UtopiaResponse::MODEL_TABLE); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php index 92003f3eeb..6fe7228ed9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -89,7 +89,7 @@ class Delete extends Action ->setContext('database', $database) ->setParam('databaseId', $databaseId) ->setParam('tableId', $table->getId()) - ->setPayload($response->output($table, UtopiaResponse::MODEL_COLLECTION)); + ->setPayload($response->output($table, UtopiaResponse::MODEL_TABLE)); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php index 2b0f77fc81..63bc1fa48c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php @@ -43,7 +43,7 @@ class Get extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLLECTION, + model: UtopiaResponse::MODEL_TABLE, ) ], contentType: ContentType::JSON @@ -69,6 +69,6 @@ class Get extends Action throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $response->dynamic($table, UtopiaResponse::MODEL_COLLECTION); + $response->dynamic($table, UtopiaResponse::MODEL_TABLE); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php index dc302ce47f..d5f8d388ab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -51,7 +51,7 @@ class Update extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLLECTION, + model: UtopiaResponse::MODEL_TABLE, ) ], contentType: ContentType::JSON @@ -105,6 +105,6 @@ class Update extends Action ->setParam('databaseId', $databaseId) ->setParam('tableId', $table->getId()); - $response->dynamic($table, UtopiaResponse::MODEL_COLLECTION); + $response->dynamic($table, UtopiaResponse::MODEL_TABLE); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index 85474840df..a5e6789eaa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -49,7 +49,7 @@ class XList extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLLECTION_LIST, + model: UtopiaResponse::MODEL_TABLE, ) ], contentType: ContentType::JSON @@ -110,8 +110,8 @@ class XList extends Action } $response->dynamic(new Document([ - 'collections' => $tables, // TODO: consider renaming to 'tables' + 'collections' => $tables, 'total' => $total, - ]), UtopiaResponse::MODEL_COLLECTION_LIST); + ]), UtopiaResponse::MODEL_TABLE_LIST); } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 0fae030b82..2ea399c450 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -40,7 +40,6 @@ use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Utopia\Response\Model\DetectionFramework; use Appwrite\Utopia\Response\Model\DetectionRuntime; use Appwrite\Utopia\Response\Model\DevKey; -use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; use Appwrite\Utopia\Response\Model\Execution; @@ -87,6 +86,7 @@ use Appwrite\Utopia\Response\Model\ProviderRepository; use Appwrite\Utopia\Response\Model\ProviderRepositoryFramework; use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntime; use Appwrite\Utopia\Response\Model\ResourceToken; +use Appwrite\Utopia\Response\Model\Row; use Appwrite\Utopia\Response\Model\Rule; use Appwrite\Utopia\Response\Model\Runtime; use Appwrite\Utopia\Response\Model\Session; @@ -164,8 +164,8 @@ class Response extends SwooleResponse public const MODEL_TABLE_LIST = 'tableList'; public const MODEL_INDEX = 'index'; public const MODEL_INDEX_LIST = 'indexList'; - public const MODEL_DOCUMENT = 'document'; - public const MODEL_DOCUMENT_LIST = 'documentList'; + public const MODEL_ROW = 'row'; + public const MODEL_ROW_LIST = 'rowList'; // Database Attributes public const MODEL_COLUMN = 'column'; @@ -376,7 +376,7 @@ class Response extends SwooleResponse ->setModel(new Error()) ->setModel(new ErrorDev()) // Lists - ->setModel(new BaseList('Rows List', self::MODEL_DOCUMENT_LIST, 'rows', self::MODEL_DOCUMENT)) + ->setModel(new BaseList('Rows List', self::MODEL_ROW_LIST, 'rows', self::MODEL_ROW)) ->setModel(new BaseList('Tables List', self::MODEL_TABLE_LIST, 'tables', self::MODEL_TABLE)) ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) @@ -442,7 +442,7 @@ class Response extends SwooleResponse ->setModel(new ColumnDatetime()) ->setModel(new ColumnRelationship()) ->setModel(new Index()) - ->setModel(new ModelDocument()) + ->setModel(new Row()) ->setModel(new Log()) ->setModel(new User()) ->setModel(new AlgoMd5()) diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Row.php similarity index 73% rename from src/Appwrite/Utopia/Response/Model/Document.php rename to src/Appwrite/Utopia/Response/Model/Row.php index 41a10cee89..49325bdc81 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Utopia\Database\Document as DatabaseDocument; -class Document extends Any +class Row extends Any { /** * Get Name @@ -14,7 +14,7 @@ class Document extends Any */ public function getName(): string { - return 'Document'; + return 'Row'; } /** @@ -24,7 +24,7 @@ class Document extends Any */ public function getType(): string { - return Response::MODEL_DOCUMENT; + return Response::MODEL_ROW; } public function __construct() @@ -32,13 +32,13 @@ class Document extends Any $this ->addRule('$id', [ 'type' => self::TYPE_STRING, - 'description' => 'Document ID.', + 'description' => 'Row ID.', 'default' => '', 'example' => '5e5ea5c16897e', ]) ->addRule('$collectionId', [ 'type' => self::TYPE_STRING, - 'description' => 'Collection ID.', + 'description' => 'Table ID.', 'default' => '', 'example' => '5e5ea5c15117e', ]) @@ -50,19 +50,19 @@ class Document extends Any ]) ->addRule('$createdAt', [ 'type' => self::TYPE_DATETIME, - 'description' => 'Document creation date in ISO 8601 format.', + 'description' => 'Row creation date in ISO 8601 format.', 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]) ->addRule('$updatedAt', [ 'type' => self::TYPE_DATETIME, - 'description' => 'Document update date in ISO 8601 format.', + 'description' => 'Row update date in ISO 8601 format.', 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]) ->addRule('$permissions', [ 'type' => self::TYPE_STRING, - 'description' => 'Document permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', + 'description' => 'Row permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', 'default' => '', 'example' => ['read("any")'], 'array' => true, @@ -75,15 +75,15 @@ class Document extends Any $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); - foreach ($document->getAttributes() as $attribute) { - if (\is_array($attribute)) { - foreach ($attribute as $subAttribute) { + foreach ($document->getAttributes() as $column) { + if (\is_array($column)) { + foreach ($column as $subAttribute) { if ($subAttribute instanceof DatabaseDocument) { $this->filter($subAttribute); } } - } elseif ($attribute instanceof DatabaseDocument) { - $this->filter($attribute); + } elseif ($column instanceof DatabaseDocument) { + $this->filter($column); } } diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index d79a104c90..3dd1bcadc7 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -22,8 +22,8 @@ class BuilderTest extends TestCase */ public function testCreateTypeMapping() { - $model = $this->response->getModel(Response::MODEL_COLLECTION); + $model = $this->response->getModel(Response::MODEL_TABLE); $type = Mapper::model(\ucfirst($model->getType())); - $this->assertEquals('Collection', $type->name); + $this->assertEquals('Table', $type->name); } } From cabf32559a723d28c6f8f1b6fb09bb2a3678d7e6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 09:50:57 +0530 Subject: [PATCH 028/173] update: return keys. --- .../Platform/Modules/Databases/Http/Columns/XList.php | 2 +- .../Modules/Databases/Http/Databases/Usage/Get.php | 8 ++++---- .../Modules/Databases/Http/Databases/Usage/XList.php | 8 ++++---- .../Platform/Modules/Databases/Http/Rows/Update.php | 10 +++++----- .../Platform/Modules/Databases/Http/Rows/XList.php | 2 +- .../Modules/Databases/Http/Tables/Usage/Get.php | 4 ++-- .../Platform/Modules/Databases/Http/Tables/XList.php | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php index cba6e2d077..e9800e71a0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -118,7 +118,7 @@ class XList extends Action } $response->dynamic(new Document([ - 'attributes' => $columns, + 'columns' => $columns, 'total' => $total, ]), UtopiaResponse::MODEL_COLUMN_LIST); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index c4105effa0..c7ae2e4d81 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -123,13 +123,13 @@ class Get extends Action $response->dynamic(new Document([ 'range' => $range, - 'collectionsTotal' => $usage[$metrics[0]]['total'], - 'documentsTotal' => $usage[$metrics[1]]['total'], + 'tablesTotal' => $usage[$metrics[0]]['total'], + 'rowsTotal' => $usage[$metrics[1]]['total'], 'storageTotal' => $usage[$metrics[2]]['total'], 'databaseReadsTotal' => $usage[$metrics[3]]['total'], 'databaseWritesTotal' => $usage[$metrics[4]]['total'], - 'collections' => $usage[$metrics[0]]['data'], - 'documents' => $usage[$metrics[1]]['data'], + 'tables' => $usage[$metrics[0]]['data'], + 'rows' => $usage[$metrics[1]]['data'], 'storage' => $usage[$metrics[2]]['data'], 'databaseReads' => $usage[$metrics[3]]['data'], 'databaseWrites' => $usage[$metrics[4]]['data'], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index 0078d54a4b..8737585e8c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -116,14 +116,14 @@ class XList extends Action $response->dynamic(new Document([ 'range' => $range, 'databasesTotal' => $usage[$metrics[0]]['total'], - 'collectionsTotal' => $usage[$metrics[1]]['total'], - 'documentsTotal' => $usage[$metrics[2]]['total'], + 'tablesTotal' => $usage[$metrics[1]]['total'], + 'rowsTotal' => $usage[$metrics[2]]['total'], 'storageTotal' => $usage[$metrics[3]]['total'], 'databasesReadsTotal' => $usage[$metrics[4]]['total'], 'databasesWritesTotal' => $usage[$metrics[5]]['total'], 'databases' => $usage[$metrics[0]]['data'], - 'collections' => $usage[$metrics[1]]['data'], - 'documents' => $usage[$metrics[2]]['data'], + 'tables' => $usage[$metrics[1]]['data'], + 'rows' => $usage[$metrics[2]]['data'], 'storage' => $usage[$metrics[3]]['data'], 'databasesReads' => $usage[$metrics[4]]['data'], 'databasesWrites' => $usage[$metrics[5]]['data'], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index b7f3a4f6c4..c9ed71d569 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -150,17 +150,17 @@ class Update extends Action $operations = 0; - $setTable = (function (Document $collection, Document $document) use (&$setTable, $dbForProject, $database, &$operations) { + $setTable = (function (Document $table, Document $row) use (&$setTable, $dbForProject, $database, &$operations) { $operations++; $relationships = \array_filter( - $collection->getAttribute('attributes', []), + $table->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); + $related = $row->getAttribute($relationship->getAttribute('key')); if (empty($related)) { continue; @@ -212,9 +212,9 @@ class Update extends Action } if ($isList) { - $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); } else { - $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + $row->setAttribute($relationship->getAttribute('key'), \reset($relations)); } } }); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php index e93d72cad6..4396c2b112 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -217,7 +217,7 @@ class XList extends Action $response->dynamic(new Document([ 'total' => $total, - 'documents' => $rows, + 'rows' => $rows, ]), UtopiaResponse::MODEL_ROW_LIST); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php index 3943c27618..b55fa84282 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -124,8 +124,8 @@ class Get extends Action $response->dynamic(new Document([ 'range' => $range, - 'documentsTotal' => $usage[$metrics[0]]['total'], - 'documents' => $usage[$metrics[0]]['data'], + 'rows' => $usage[$metrics[0]]['data'], + 'rowsTotal' => $usage[$metrics[0]]['total'], ]), UtopiaResponse::MODEL_USAGE_COLLECTION); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index a5e6789eaa..86107cc533 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -110,7 +110,7 @@ class XList extends Action } $response->dynamic(new Document([ - 'collections' => $tables, + 'tables' => $tables, 'total' => $total, ]), UtopiaResponse::MODEL_TABLE_LIST); } From 6cb44399c147cffe0dfb69dc0e4276da9230bdcd Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 10:57:54 +0530 Subject: [PATCH 029/173] update: services. --- app/config/services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/services.php b/app/config/services.php index 7e4656a277..edc4024272 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -58,7 +58,7 @@ return [ 'name' => 'Databases', 'subtitle' => 'The Databases service allows you to create structured collections of documents, query and filter lists of documents', 'description' => '/docs/services/databases.md', - 'controller' => 'api/databases.php', + 'controller' => '', // Uses modules 'sdk' => true, 'docs' => true, 'docsUrl' => 'https://appwrite.io/docs/client/databases', From 71ad9c203e7d03be19958618a71ba234d34959ec Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 13:53:57 +0530 Subject: [PATCH 030/173] fix: response modals, mappings, filters. --- .../Databases/Http/Tables/Usage/Get.php | 4 +- src/Appwrite/Utopia/Request/Filters/V19.php | 19 +++-- src/Appwrite/Utopia/Response.php | 6 +- src/Appwrite/Utopia/Response/Filters/V19.php | 77 ++++++++++++++++++- .../Response/Model/ColumnRelationship.php | 4 +- src/Appwrite/Utopia/Response/Model/Table.php | 38 +++++++++ .../Utopia/Response/Model/UsageDatabase.php | 16 ++-- .../Utopia/Response/Model/UsageDatabases.php | 16 ++-- .../{UsageCollection.php => UsageTable.php} | 14 ++-- 9 files changed, 152 insertions(+), 42 deletions(-) rename src/Appwrite/Utopia/Response/Model/{UsageCollection.php => UsageTable.php} (77%) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php index b55fa84282..4c591bff1a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -47,7 +47,7 @@ class Get extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_USAGE_COLLECTION, + model: UtopiaResponse::MODEL_USAGE_TABLE, ) ], contentType: ContentType::JSON, @@ -126,6 +126,6 @@ class Get extends Action 'range' => $range, 'rows' => $usage[$metrics[0]]['data'], 'rowsTotal' => $usage[$metrics[0]]['total'], - ]), UtopiaResponse::MODEL_USAGE_COLLECTION); + ]), UtopiaResponse::MODEL_USAGE_TABLE); } } diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index 041c126a69..ee04cea038 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -9,15 +9,18 @@ class V19 extends Filter // Convert 1.6 params to 1.7 public function parse(array $content, string $model): array { - /* - Uncomment with first request filter; current is just a copy of V18 - switch ($model) { - case 'functions.create': - $content['something'] = $content['somethingElse'] ?? ""; - unset($content['something']); - break; + return match ($model) { + 'databases.createRelationshipColumn' => $this->convertV16RelationshipParams($content), + default => $content + }; + } + + protected function convertV16RelationshipParams(array $content): array + { + if (isset($content['relatedCollectionId'])) { + $content['relatedTableId'] = $content['relatedCollectionId']; + unset($content['relatedCollectionId']); } - */ return $content; } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 2ea399c450..62822e9956 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -106,7 +106,6 @@ use Appwrite\Utopia\Response\Model\TemplateVariable; use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Topic; use Appwrite\Utopia\Response\Model\UsageBuckets; -use Appwrite\Utopia\Response\Model\UsageCollection; use Appwrite\Utopia\Response\Model\UsageDatabase; use Appwrite\Utopia\Response\Model\UsageDatabases; use Appwrite\Utopia\Response\Model\UsageFunction; @@ -115,6 +114,7 @@ use Appwrite\Utopia\Response\Model\UsageProject; use Appwrite\Utopia\Response\Model\UsageSite; use Appwrite\Utopia\Response\Model\UsageSites; use Appwrite\Utopia\Response\Model\UsageStorage; +use Appwrite\Utopia\Response\Model\UsageTable; use Appwrite\Utopia\Response\Model\UsageUsers; use Appwrite\Utopia\Response\Model\User; use Appwrite\Utopia\Response\Model\Variable; @@ -147,7 +147,7 @@ class Response extends SwooleResponse public const MODEL_BASE_LIST = 'baseList'; public const MODEL_USAGE_DATABASES = 'usageDatabases'; public const MODEL_USAGE_DATABASE = 'usageDatabase'; - public const MODEL_USAGE_COLLECTION = 'usageCollection'; + public const MODEL_USAGE_TABLE = 'usageTable'; public const MODEL_USAGE_USERS = 'usageUsers'; public const MODEL_USAGE_BUCKETS = 'usageBuckets'; public const MODEL_USAGE_STORAGE = 'usageStorage'; @@ -508,7 +508,7 @@ class Response extends SwooleResponse ->setModel(new MetricBreakdown()) ->setModel(new UsageDatabases()) ->setModel(new UsageDatabase()) - ->setModel(new UsageCollection()) + ->setModel(new UsageTable()) ->setModel(new UsageUsers()) ->setModel(new UsageStorage()) ->setModel(new UsageBuckets()) diff --git a/src/Appwrite/Utopia/Response/Filters/V19.php b/src/Appwrite/Utopia/Response/Filters/V19.php index 2987c5a7e4..5e1a7e40e7 100644 --- a/src/Appwrite/Utopia/Response/Filters/V19.php +++ b/src/Appwrite/Utopia/Response/Filters/V19.php @@ -7,24 +7,93 @@ use Appwrite\Utopia\Response\Filter; class V19 extends Filter { + private const DATABASE_MAPPINGS = [ + 'table' => 'collection', + 'tables' => 'collections', + 'tablesTotal' => 'collectionsTotal', + 'relatedTable' => 'relatedCollection', + + 'column' => 'attribute', + 'columns' => 'attributes', + 'columnsTotal' => 'attributesTotal', + + 'row' => 'document', + 'rows' => 'documents', + 'rowsTotal' => 'documentsTotal' + ]; + // Convert 1.7 Data format to 1.6 format public function parse(array $content, string $model): array { $parsedResponse = $content; - $parsedResponse = match($model) { + return match ($model) { + Response::MODEL_ROW, + Response::MODEL_TABLE, + Response::MODEL_COLUMN, + Response::MODEL_ROW_LIST, + Response::MODEL_TABLE_LIST, + Response::MODEL_COLUMN_LIST, + Response::MODEL_USAGE_TABLE, + Response::MODEL_USAGE_DATABASE, + Response::MODEL_USAGE_DATABASES, + Response::MODEL_COLUMN_RELATIONSHIP => $this->handleDBTerminology($model, $content), + Response::MODEL_FUNCTION => $this->parseFunction($content), Response::MODEL_FUNCTION_LIST => $this->handleList($content, 'functions', fn ($item) => $this->parseFunction($item)), default => $parsedResponse, }; - - return $parsedResponse; } - protected function parseFunction(array $content) + protected function parseFunction(array $content): array { $content['deployment'] = $content['deploymentId'] ?? ''; unset($content['deploymentId']); return $content; } + + protected function handleDBTerminology(string $model, array $content): array + { + $isListModel = match ($model) { + Response::MODEL_ROW_LIST, + Response::MODEL_TABLE_LIST, + Response::MODEL_COLUMN_LIST => true, + + default => false + }; + + if ($isListModel) { + foreach (self::DATABASE_MAPPINGS as $oldKey => $newKey) { + if (isset($content[$oldKey])) { + $content[$newKey] = array_map(fn ($item) => $this->remapKeys($item), $content[$oldKey]); + unset($content[$oldKey]); + } + } + } else { + $content = $this->remapKeysRecursive($content); + } + + return $content; + } + + private function remapKeys(array $data): array + { + foreach (self::DATABASE_MAPPINGS as $old => $new) { + if (isset($data[$old])) { + $data[$new] = $data[$old]; + unset($data[$old]); + } + } + return $data; + } + + private function remapKeysRecursive(array $data): array + { + $result = []; + foreach ($data as $key => $value) { + $newKey = self::DATABASE_MAPPINGS[$key] ?? $key; + $result[$newKey] = \is_array($value) ? $this->remapKeysRecursive($value) : $value; + } + return $result; + } } diff --git a/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php index 2c5649b5bf..877982365b 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php @@ -12,7 +12,7 @@ class ColumnRelationship extends Column parent::__construct(); $this - ->addRule('relatedCollection', [ + ->addRule('relatedTable', [ 'type' => self::TYPE_STRING, 'description' => 'The ID of the related table.', 'default' => null, @@ -84,7 +84,7 @@ class ColumnRelationship extends Column { $options = $document->getAttribute('options'); if (!\is_null($options)) { - $document->setAttribute('relatedCollection', $options['relatedCollection']); + $document->setAttribute('relatedTable', $options['relatedCollection']); $document->setAttribute('relationType', $options['relationType']); $document->setAttribute('twoWay', $options['twoWay']); $document->setAttribute('twoWayKey', $options['twoWayKey']); diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index d7098f313f..e36cb1dabd 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Database\Document; class Table extends Model { @@ -106,4 +107,41 @@ class Table extends Model { return Response::MODEL_TABLE; } + + /** + * Process Document before returning it to the client for backwards compatibility! + */ + public function filter(Document $document): Document + { + $columns = $document->getAttribute('attributes', []); + if (!empty($columns) && \is_array($columns)) { + $columns = $this->remapNestedRelatedCollections($columns); + } + + $document + ->setAttribute('columns', $columns) + ->removeAttribute('attributes'); + + $related = $document->getAttribute('relatedCollection'); + if ($related !== null) { + $document + ->setAttribute('relatedTable', $related) + ->removeAttribute('relatedCollection'); + } + + return $document; + } + + // 1.7 now sends back `relatedTable` instead of `relatedCollection`. + // This is necessary because the actual database underneath uses `relatedCollection`. + private function remapNestedRelatedCollections(array $columns): array + { + foreach ($columns as $i => $column) { + if (isset($column['relatedCollection'])) { + $columns[$i]['relatedTable'] = $column['relatedCollection']; + unset($columns[$i]['relatedCollection']); + } + } + return $columns; + } } diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index a0fe421f5f..a3212017a4 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -16,15 +16,15 @@ class UsageDatabase extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('collectionsTotal', [ + ->addRule('tablesTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of collections.', + 'description' => 'Total aggregated number of tables.', 'default' => 0, 'example' => 0, ]) - ->addRule('documentsTotal', [ + ->addRule('rowsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of documents.', + 'description' => 'Total aggregated number of rows.', 'default' => 0, 'example' => 0, ]) @@ -46,16 +46,16 @@ class UsageDatabase extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('collections', [ + ->addRule('tables', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of collections per period.', + 'description' => 'Aggregated number of tables per period.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documents', [ + ->addRule('rows', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of documents per period.', + 'description' => 'Aggregated number of rows per period.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php index 4e053e5223..11392f6efb 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php @@ -22,15 +22,15 @@ class UsageDatabases extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('collectionsTotal', [ + ->addRule('tablesTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of collections.', + 'description' => 'Total aggregated number of tables.', 'default' => 0, 'example' => 0, ]) - ->addRule('documentsTotal', [ + ->addRule('rowsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of documents.', + 'description' => 'Total aggregated number of rows.', 'default' => 0, 'example' => 0, ]) @@ -59,16 +59,16 @@ class UsageDatabases extends Model 'example' => [], 'array' => true ]) - ->addRule('collections', [ + ->addRule('tables', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of collections per period.', + 'description' => 'Aggregated number of tables per period.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documents', [ + ->addRule('rows', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of documents per period.', + 'description' => 'Aggregated number of rows per period.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageCollection.php b/src/Appwrite/Utopia/Response/Model/UsageTable.php similarity index 77% rename from src/Appwrite/Utopia/Response/Model/UsageCollection.php rename to src/Appwrite/Utopia/Response/Model/UsageTable.php index b2336d65ba..553a4b5001 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageCollection.php +++ b/src/Appwrite/Utopia/Response/Model/UsageTable.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -class UsageCollection extends Model +class UsageTable extends Model { public function __construct() { @@ -16,15 +16,15 @@ class UsageCollection extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('documentsTotal', [ + ->addRule('rowsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of of documents.', + 'description' => 'Total aggregated number of of rows.', 'default' => 0, 'example' => 0, ]) - ->addRule('documents', [ + ->addRule('rows', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of documents per period.', + 'description' => 'Aggregated number of rows per period.', 'default' => [], 'example' => [], 'array' => true @@ -39,7 +39,7 @@ class UsageCollection extends Model */ public function getName(): string { - return 'UsageCollection'; + return 'UsageTable'; } /** @@ -49,6 +49,6 @@ class UsageCollection extends Model */ public function getType(): string { - return Response::MODEL_USAGE_COLLECTION; + return Response::MODEL_USAGE_TABLE; } } From d30fd565733df843c038eb83b7ae418f09ac0901 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 14:04:39 +0530 Subject: [PATCH 031/173] patch: index module to use/specify `columns` instead of `attributes`. --- src/Appwrite/Utopia/Response/Model/Index.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Index.php b/src/Appwrite/Utopia/Response/Model/Index.php index 2d795ad439..e18a9db334 100644 --- a/src/Appwrite/Utopia/Response/Model/Index.php +++ b/src/Appwrite/Utopia/Response/Model/Index.php @@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Database\Document; class Index extends Model { @@ -34,7 +35,7 @@ class Index extends Model 'default' => '', 'example' => 'string', ]) - ->addRule('attributes', [ + ->addRule('columns', [ 'type' => self::TYPE_STRING, 'description' => 'Index attributes.', 'default' => [], @@ -78,4 +79,16 @@ class Index extends Model { return Response::MODEL_INDEX; } + + public function filter(Document $document): Document + { + + $columns = $document->getAttribute('attributes', []); + $document + ->removeAttribute('attributes') + ->setAttribute('columns', $columns); + + return $document; + + } } From e2202e9ffa8a9fdbffd93257e251b28d2c25ff2d Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 14:05:24 +0530 Subject: [PATCH 032/173] remove: old attributes anyway. --- src/Appwrite/Utopia/Response/Model/Table.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index e36cb1dabd..1855f471b1 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -118,17 +118,18 @@ class Table extends Model $columns = $this->remapNestedRelatedCollections($columns); } - $document - ->setAttribute('columns', $columns) - ->removeAttribute('attributes'); + $document->setAttribute('columns', $columns); $related = $document->getAttribute('relatedCollection'); if ($related !== null) { - $document - ->setAttribute('relatedTable', $related) - ->removeAttribute('relatedCollection'); + $document->setAttribute('relatedTable', $related); } + // remove anyways as they are already copied above. + $document + ->removeAttribute('attributes') + ->removeAttribute('relatedCollection'); + return $document; } From 75c9db8031bd0525053f879dbec13d11f7c5c43e Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 14:07:34 +0530 Subject: [PATCH 033/173] update: tests to use the 1.7.x changes on databases. --- .../e2e/Services/Databases/DatabasesBase.php | 326 +++++++++--------- .../Databases/DatabasesConsoleClientTest.php | 12 +- .../Databases/DatabasesCustomClientTest.php | 4 +- .../Databases/DatabasesCustomServerTest.php | 172 ++++----- .../DatabasesPermissionsGuestTest.php | 4 +- .../DatabasesPermissionsMemberTest.php | 4 +- .../DatabasesPermissionsTeamTest.php | 2 +- 7 files changed, 262 insertions(+), 262 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 989d1217d3..14713a52ee 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -339,7 +339,7 @@ trait DatabasesBase $this->assertEquals($relationship['headers']['status-code'], 202); $this->assertEquals($relationship['body']['key'], 'starringActors'); $this->assertEquals($relationship['body']['type'], 'relationship'); - $this->assertEquals($relationship['body']['relatedCollection'], $data['actorsId']); + $this->assertEquals($relationship['body']['relatedTable'], $data['actorsId']); $this->assertEquals($relationship['body']['relationType'], 'oneToMany'); $this->assertEquals($relationship['body']['twoWay'], true); $this->assertEquals($relationship['body']['twoWayKey'], 'movie'); @@ -360,17 +360,17 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertIsArray($movies['body']['attributes']); - $this->assertCount(9, $movies['body']['attributes']); - $this->assertEquals($movies['body']['attributes'][0]['key'], $title['body']['key']); - $this->assertEquals($movies['body']['attributes'][1]['key'], $description['body']['key']); - $this->assertEquals($movies['body']['attributes'][2]['key'], $tagline['body']['key']); - $this->assertEquals($movies['body']['attributes'][3]['key'], $releaseYear['body']['key']); - $this->assertEquals($movies['body']['attributes'][4]['key'], $duration['body']['key']); - $this->assertEquals($movies['body']['attributes'][5]['key'], $actors['body']['key']); - $this->assertEquals($movies['body']['attributes'][6]['key'], $datetime['body']['key']); - $this->assertEquals($movies['body']['attributes'][7]['key'], $relationship['body']['key']); - $this->assertEquals($movies['body']['attributes'][8]['key'], $integers['body']['key']); + $this->assertIsArray($movies['body']['columns']); + $this->assertCount(9, $movies['body']['columns']); + $this->assertEquals($movies['body']['columns'][0]['key'], $title['body']['key']); + $this->assertEquals($movies['body']['columns'][1]['key'], $description['body']['key']); + $this->assertEquals($movies['body']['columns'][2]['key'], $tagline['body']['key']); + $this->assertEquals($movies['body']['columns'][3]['key'], $releaseYear['body']['key']); + $this->assertEquals($movies['body']['columns'][4]['key'], $duration['body']['key']); + $this->assertEquals($movies['body']['columns'][5]['key'], $actors['body']['key']); + $this->assertEquals($movies['body']['columns'][6]['key'], $datetime['body']['key']); + $this->assertEquals($movies['body']['columns'][7]['key'], $relationship['body']['key']); + $this->assertEquals($movies['body']['columns'][8]['key'], $integers['body']['key']); return $data; } @@ -393,7 +393,7 @@ trait DatabasesBase ], ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(2, \count($response['body']['attributes'])); + $this->assertEquals(2, \count($response['body']['columns'])); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -449,7 +449,7 @@ trait DatabasesBase ]), [ 'key' => 'titleIndex', 'type' => 'key', - 'attributes' => ['title'], + 'columns' => ['title'], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -763,7 +763,7 @@ trait DatabasesBase $this->assertEquals('relationship', $relationship['body']['type']); $this->assertEquals(false, $relationship['body']['required']); $this->assertEquals(false, $relationship['body']['array']); - $this->assertEquals($data['actorsId'], $relationship['body']['relatedCollection']); + $this->assertEquals($data['actorsId'], $relationship['body']['relatedTable']); $this->assertEquals('oneToMany', $relationship['body']['relationType']); $this->assertEquals(true, $relationship['body']['twoWay']); $this->assertEquals('twoWayKey', $relationship['body']['twoWayKey']); @@ -947,7 +947,7 @@ trait DatabasesBase $this->assertEquals('available', $relationshipResponse['body']['status']); $this->assertEquals($relationship['body']['required'], $relationshipResponse['body']['required']); $this->assertEquals($relationship['body']['array'], $relationshipResponse['body']['array']); - $this->assertEquals($relationship['body']['relatedCollection'], $relationshipResponse['body']['relatedCollection']); + $this->assertEquals($relationship['body']['relatedTable'], $relationshipResponse['body']['relatedTable']); $this->assertEquals($relationship['body']['relationType'], $relationshipResponse['body']['relationType']); $this->assertEquals($relationship['body']['twoWay'], $relationshipResponse['body']['twoWay']); $this->assertEquals($relationship['body']['twoWayKey'], $relationshipResponse['body']['twoWayKey']); @@ -961,7 +961,7 @@ trait DatabasesBase $this->assertEquals(200, $attributes['headers']['status-code']); $this->assertEquals(12, $attributes['body']['total']); - $attributes = $attributes['body']['attributes']; + $attributes = $attributes['body']['columns']; $this->assertIsArray($attributes); $this->assertCount(12, $attributes); @@ -1043,7 +1043,7 @@ trait DatabasesBase $this->assertEquals($relationshipResponse['body']['status'], $attributes[9]['status']); $this->assertEquals($relationshipResponse['body']['required'], $attributes[9]['required']); $this->assertEquals($relationshipResponse['body']['array'], $attributes[9]['array']); - $this->assertEquals($relationshipResponse['body']['relatedCollection'], $attributes[9]['relatedCollection']); + $this->assertEquals($relationshipResponse['body']['relatedTable'], $attributes[9]['relatedTable']); $this->assertEquals($relationshipResponse['body']['relationType'], $attributes[9]['relationType']); $this->assertEquals($relationshipResponse['body']['twoWay'], $attributes[9]['twoWay']); $this->assertEquals($relationshipResponse['body']['twoWayKey'], $attributes[9]['twoWayKey']); @@ -1072,7 +1072,7 @@ trait DatabasesBase $this->assertEquals(200, $collection['headers']['status-code']); - $attributes = $collection['body']['attributes']; + $attributes = $collection['body']['columns']; $this->assertIsArray($attributes); $this->assertCount(12, $attributes); @@ -1155,7 +1155,7 @@ trait DatabasesBase $this->assertEquals($relationshipResponse['body']['status'], $attributes[9]['status']); $this->assertEquals($relationshipResponse['body']['required'], $attributes[9]['required']); $this->assertEquals($relationshipResponse['body']['array'], $attributes[9]['array']); - $this->assertEquals($relationshipResponse['body']['relatedCollection'], $attributes[9]['relatedCollection']); + $this->assertEquals($relationshipResponse['body']['relatedTable'], $attributes[9]['relatedTable']); $this->assertEquals($relationshipResponse['body']['relationType'], $attributes[9]['relationType']); $this->assertEquals($relationshipResponse['body']['twoWay'], $attributes[9]['twoWay']); $this->assertEquals($relationshipResponse['body']['twoWayKey'], $attributes[9]['twoWayKey']); @@ -1210,14 +1210,14 @@ trait DatabasesBase ]), [ 'key' => 'titleIndex', 'type' => 'fulltext', - 'attributes' => ['title'], + 'columns' => ['title'], ]); $this->assertEquals(202, $titleIndex['headers']['status-code']); $this->assertEquals('titleIndex', $titleIndex['body']['key']); $this->assertEquals('fulltext', $titleIndex['body']['type']); - $this->assertCount(1, $titleIndex['body']['attributes']); - $this->assertEquals('title', $titleIndex['body']['attributes'][0]); + $this->assertCount(1, $titleIndex['body']['columns']); + $this->assertEquals('title', $titleIndex['body']['columns'][0]); $releaseYearIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1226,14 +1226,14 @@ trait DatabasesBase ]), [ 'key' => 'releaseYear', 'type' => 'key', - 'attributes' => ['releaseYear'], + 'columns' => ['releaseYear'], ]); $this->assertEquals(202, $releaseYearIndex['headers']['status-code']); $this->assertEquals('releaseYear', $releaseYearIndex['body']['key']); $this->assertEquals('key', $releaseYearIndex['body']['type']); - $this->assertCount(1, $releaseYearIndex['body']['attributes']); - $this->assertEquals('releaseYear', $releaseYearIndex['body']['attributes'][0]); + $this->assertCount(1, $releaseYearIndex['body']['columns']); + $this->assertEquals('releaseYear', $releaseYearIndex['body']['columns'][0]); $releaseWithDate1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1242,16 +1242,16 @@ trait DatabasesBase ]), [ 'key' => 'releaseYearDated', 'type' => 'key', - 'attributes' => ['releaseYear', '$createdAt', '$updatedAt'], + 'columns' => ['releaseYear', '$createdAt', '$updatedAt'], ]); $this->assertEquals(202, $releaseWithDate1['headers']['status-code']); $this->assertEquals('releaseYearDated', $releaseWithDate1['body']['key']); $this->assertEquals('key', $releaseWithDate1['body']['type']); - $this->assertCount(3, $releaseWithDate1['body']['attributes']); - $this->assertEquals('releaseYear', $releaseWithDate1['body']['attributes'][0]); - $this->assertEquals('$createdAt', $releaseWithDate1['body']['attributes'][1]); - $this->assertEquals('$updatedAt', $releaseWithDate1['body']['attributes'][2]); + $this->assertCount(3, $releaseWithDate1['body']['columns']); + $this->assertEquals('releaseYear', $releaseWithDate1['body']['columns'][0]); + $this->assertEquals('$createdAt', $releaseWithDate1['body']['columns'][1]); + $this->assertEquals('$updatedAt', $releaseWithDate1['body']['columns'][2]); $releaseWithDate2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1260,14 +1260,14 @@ trait DatabasesBase ]), [ 'key' => 'birthDay', 'type' => 'key', - 'attributes' => ['birthDay'], + 'columns' => ['birthDay'], ]); $this->assertEquals(202, $releaseWithDate2['headers']['status-code']); $this->assertEquals('birthDay', $releaseWithDate2['body']['key']); $this->assertEquals('key', $releaseWithDate2['body']['type']); - $this->assertCount(1, $releaseWithDate2['body']['attributes']); - $this->assertEquals('birthDay', $releaseWithDate2['body']['attributes'][0]); + $this->assertCount(1, $releaseWithDate2['body']['columns']); + $this->assertEquals('birthDay', $releaseWithDate2['body']['columns'][0]); // Test for failure $fulltextReleaseYear = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ @@ -1277,7 +1277,7 @@ trait DatabasesBase ]), [ 'key' => 'releaseYearDated', 'type' => 'fulltext', - 'attributes' => ['releaseYear'], + 'columns' => ['releaseYear'], ]); $this->assertEquals(400, $fulltextReleaseYear['headers']['status-code']); @@ -1290,7 +1290,7 @@ trait DatabasesBase ]), [ 'key' => 'none', 'type' => 'key', - 'attributes' => [], + 'columns' => [], ]); $this->assertEquals(400, $noAttributes['headers']['status-code']); @@ -1303,7 +1303,7 @@ trait DatabasesBase ]), [ 'key' => 'duplicate', 'type' => 'fulltext', - 'attributes' => ['releaseYear', 'releaseYear'], + 'columns' => ['releaseYear', 'releaseYear'], ]); $this->assertEquals(400, $duplicates['headers']['status-code']); @@ -1316,7 +1316,7 @@ trait DatabasesBase ]), [ 'key' => 'tooLong', 'type' => 'key', - 'attributes' => ['description', 'tagline'], + 'columns' => ['description', 'tagline'], ]); $this->assertEquals(400, $tooLong['headers']['status-code']); @@ -1329,7 +1329,7 @@ trait DatabasesBase ]), [ 'key' => 'ft', 'type' => 'fulltext', - 'attributes' => ['actors'], + 'columns' => ['actors'], ]); $this->assertEquals(400, $fulltextArray['headers']['status-code']); @@ -1342,7 +1342,7 @@ trait DatabasesBase ]), [ 'key' => 'index-actors', 'type' => 'key', - 'attributes' => ['actors'], + 'columns' => ['actors'], ]); $this->assertEquals(202, $actorsArray['headers']['status-code']); @@ -1354,7 +1354,7 @@ trait DatabasesBase ]), [ 'key' => 'index-ip-actors', 'type' => 'key', - 'attributes' => ['releaseYear', 'actors'], // 2 levels + 'columns' => ['releaseYear', 'actors'], // 2 levels 'orders' => ['DESC', 'DESC'], ]); @@ -1369,7 +1369,7 @@ trait DatabasesBase ]), [ 'key' => 'index-unknown', 'type' => 'key', - 'attributes' => ['Unknown'], + 'columns' => ['Unknown'], ]); $this->assertEquals(400, $unknown['headers']['status-code']); @@ -1382,7 +1382,7 @@ trait DatabasesBase ]), [ 'key' => 'integers-order', 'type' => 'key', - 'attributes' => ['integers'], // array attribute + 'columns' => ['integers'], // array attribute 'orders' => ['DESC'], // Check order is removed in API ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -1394,7 +1394,7 @@ trait DatabasesBase ]), [ 'key' => 'integers-size', 'type' => 'key', - 'attributes' => ['integers'], // array attribute + 'columns' => ['integers'], // array attribute ]); $this->assertEquals(202, $index2['headers']['status-code']); @@ -1612,15 +1612,15 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); - $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); - $this->assertEquals(2019, $documents['body']['documents'][2]['releaseYear']); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][0])); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][1])); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][2])); - $this->assertCount(3, $documents['body']['documents']); + $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $documents['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $documents['body']['rows'][2]['releaseYear']); + $this->assertFalse(array_key_exists('$internalId', $documents['body']['rows'][0])); + $this->assertFalse(array_key_exists('$internalId', $documents['body']['rows'][1])); + $this->assertFalse(array_key_exists('$internalId', $documents['body']['rows'][2])); + $this->assertCount(3, $documents['body']['rows']); - foreach ($documents['body']['documents'] as $document) { + foreach ($documents['body']['rows'] as $document) { $this->assertEquals($data['moviesId'], $document['$collectionId']); $this->assertArrayNotHasKey('$collection', $document); $this->assertEquals($databaseId, $document['$databaseId']); @@ -1636,10 +1636,10 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['documents'][2]['releaseYear']); - $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); - $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); - $this->assertCount(3, $documents['body']['documents']); + $this->assertEquals(1944, $documents['body']['rows'][2]['releaseYear']); + $this->assertEquals(2017, $documents['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $documents['body']['rows'][0]['releaseYear']); + $this->assertCount(3, $documents['body']['rows']); // changing description attribute to be null by default instead of empty string $patchNull = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes/string/description', array_merge([ @@ -1685,7 +1685,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - return ['documents' => $documents['body']['documents'], 'databaseId' => $databaseId]; + return ['rows' => $documents['body']['rows'], 'databaseId' => $databaseId]; } @@ -1695,7 +1695,7 @@ trait DatabasesBase public function testGetDocument(array $data): void { $databaseId = $data['databaseId']; - foreach ($data['documents'] as $document) { + foreach ($data['rows'] as $document) { $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1721,7 +1721,7 @@ trait DatabasesBase public function testGetDocumentWithQueries(array $data): void { $databaseId = $data['databaseId']; - $document = $data['documents'][0]; + $document = $data['rows'][0]; $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', @@ -1753,36 +1753,36 @@ trait DatabasesBase ], $this->getHeaders())); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals('Captain America', $base['body']['documents'][0]['title']); - $this->assertEquals('Spider-Man: Far From Home', $base['body']['documents'][1]['title']); - $this->assertEquals('Spider-Man: Homecoming', $base['body']['documents'][2]['title']); - $this->assertCount(3, $base['body']['documents']); + $this->assertEquals('Captain America', $base['body']['rows'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']); + $this->assertCount(3, $base['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['documents'][0]['$id']]))->toString() + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['documents'][1]['$id'], $documents['body']['documents'][0]['$id']); - $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][1]['$id']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals($base['body']['rows'][1]['$id'], $documents['body']['rows'][0]['$id']); + $this->assertEquals($base['body']['rows'][2]['$id'], $documents['body']['rows'][1]['$id']); + $this->assertCount(2, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['documents'][2]['$id']]))->toString() + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEmpty($documents['body']['documents']); + $this->assertEmpty($documents['body']['rows']); /** * Test with ASC order and after. @@ -1797,24 +1797,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['documents'][0]['releaseYear']); - $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['documents'][2]['releaseYear']); - $this->assertCount(3, $base['body']['documents']); + $this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), Query::orderAsc('releaseYear')->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][0]['$id']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals($base['body']['rows'][2]['$id'], $documents['body']['rows'][0]['$id']); + $this->assertCount(1, $documents['body']['rows']); /** * Test with DESC order and after. @@ -1829,24 +1829,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['documents'][2]['releaseYear']); - $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['documents'][0]['releaseYear']); - $this->assertCount(3, $base['body']['documents']); + $this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), Query::orderDesc('releaseYear')->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][0]['$id']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals($base['body']['rows'][2]['$id'], $documents['body']['rows'][0]['$id']); + $this->assertCount(1, $documents['body']['rows']); /** * Test after with unknown document. @@ -1895,36 +1895,36 @@ trait DatabasesBase ], $this->getHeaders())); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals('Captain America', $base['body']['documents'][0]['title']); - $this->assertEquals('Spider-Man: Far From Home', $base['body']['documents'][1]['title']); - $this->assertEquals('Spider-Man: Homecoming', $base['body']['documents'][2]['title']); - $this->assertCount(3, $base['body']['documents']); + $this->assertEquals('Captain America', $base['body']['rows'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']); + $this->assertCount(3, $base['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['documents'][2]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']); - $this->assertEquals($base['body']['documents'][1]['$id'], $documents['body']['documents'][1]['$id']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals($base['body']['rows'][0]['$id'], $documents['body']['rows'][0]['$id']); + $this->assertEquals($base['body']['rows'][1]['$id'], $documents['body']['rows'][1]['$id']); + $this->assertCount(2, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['documents'][0]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEmpty($documents['body']['documents']); + $this->assertEmpty($documents['body']['rows']); /** * Test with ASC order and after. @@ -1939,24 +1939,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['documents'][0]['releaseYear']); - $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['documents'][2]['releaseYear']); - $this->assertCount(3, $base['body']['documents']); + $this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), Query::orderAsc('releaseYear')->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals($base['body']['rows'][0]['$id'], $documents['body']['rows'][0]['$id']); + $this->assertCount(1, $documents['body']['rows']); /** * Test with DESC order and after. @@ -1971,24 +1971,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['documents'][2]['releaseYear']); - $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['documents'][0]['releaseYear']); - $this->assertCount(3, $base['body']['documents']); + $this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), Query::orderDesc('releaseYear')->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals($base['body']['rows'][0]['$id'], $documents['body']['rows'][0]['$id']); + $this->assertCount(1, $documents['body']['rows']); return []; } @@ -2010,8 +2010,8 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2025,9 +2025,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']); - $this->assertEquals(2019, $documents['body']['documents'][1]['releaseYear']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals(2017, $documents['body']['rows'][0]['releaseYear']); + $this->assertEquals(2019, $documents['body']['rows'][1]['releaseYear']); + $this->assertCount(2, $documents['body']['rows']); return []; } @@ -2048,21 +2048,21 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::equal('$id', [$documents['body']['documents'][0]['$id']])->toString(), + Query::equal('$id', [$documents['body']['rows'][0]['$id']])->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2074,8 +2074,8 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals(2017, $documents['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2087,9 +2087,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); - $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals(2019, $documents['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $documents['body']['rows'][1]['releaseYear']); + $this->assertCount(2, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2124,8 +2124,8 @@ trait DatabasesBase ], ]); - $this->assertCount(1, $documents['body']['documents']); - $this->assertEquals('Captain America', $documents['body']['documents'][0]['title']); + $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals('Captain America', $documents['body']['rows'][0]['title']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2136,9 +2136,9 @@ trait DatabasesBase ], ]); - $this->assertCount(2, $documents['body']['documents']); - $this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['title']); - $this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['title']); + $this->assertCount(2, $documents['body']['rows']); + $this->assertEquals('Spider-Man: Far From Home', $documents['body']['rows'][0]['title']); + $this->assertEquals('Spider-Man: Homecoming', $documents['body']['rows'][1]['title']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2149,7 +2149,7 @@ trait DatabasesBase ], ]); - $this->assertCount(3, $documents['body']['documents']); + $this->assertCount(3, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2160,7 +2160,7 @@ trait DatabasesBase ], ]); - $this->assertCount(0, $documents['body']['documents']); + $this->assertCount(0, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2208,9 +2208,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals('1975-06-12T12:12:55.000+00:00', $documents['body']['documents'][0]['birthDay']); - $this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['documents'][1]['birthDay']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals('1975-06-12T12:12:55.000+00:00', $documents['body']['rows'][0]['birthDay']); + $this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['rows'][1]['birthDay']); + $this->assertCount(2, $documents['body']['rows']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2682,7 +2682,7 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'], ]), []); - $this->assertCount(10, $collection['body']['attributes']); + $this->assertCount(10, $collection['body']['columns']); /** * Test for successful validation @@ -3187,7 +3187,7 @@ trait DatabasesBase ]), [ 'key' => 'key_attribute', 'type' => 'key', - 'attributes' => [$attribute['body']['key']], + 'columns' => [$attribute['body']['key']], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -3253,7 +3253,7 @@ trait DatabasesBase // Current user has read permission on the collection so can get any document $this->assertEquals(3, $documentsUser1['body']['total']); - $this->assertCount(3, $documentsUser1['body']['documents']); + $this->assertCount(3, $documentsUser1['body']['rows']); $document3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], array_merge([ 'content-type' => 'application/json', @@ -3315,7 +3315,7 @@ trait DatabasesBase // Current user has no collection permissions but has read permission for one document $this->assertEquals(1, $documentsUser2['body']['total']); - $this->assertCount(1, $documentsUser2['body']['documents']); + $this->assertCount(1, $documentsUser2['body']['rows']); } public function testEnforceCollectionPermissions() @@ -3376,7 +3376,7 @@ trait DatabasesBase ]), [ 'key' => 'key_attribute', 'type' => 'key', - 'attributes' => [$attribute['body']['key']], + 'columns' => [$attribute['body']['key']], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -3441,7 +3441,7 @@ trait DatabasesBase // Current user has read permission on the collection so can get any document $this->assertEquals(3, $documentsUser1['body']['total']); - $this->assertCount(3, $documentsUser1['body']['documents']); + $this->assertCount(3, $documentsUser1['body']['rows']); $document3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], array_merge([ 'content-type' => 'application/json', @@ -3513,7 +3513,7 @@ trait DatabasesBase // Current user has no collection permissions read access to one document $this->assertEquals(1, $documentsUser2['body']['total']); - $this->assertCount(1, $documentsUser2['body']['documents']); + $this->assertCount(1, $documentsUser2['body']['rows']); } /** @@ -3529,7 +3529,7 @@ trait DatabasesBase ]), [ 'key' => 'unique_title', 'type' => 'unique', - 'attributes' => ['title'], + 'columns' => ['title'], ]); $this->assertEquals(202, $uniqueIndex['headers']['status-code']); @@ -3929,8 +3929,8 @@ trait DatabasesBase $this->assertEquals(200, $attributes['headers']['status-code']); $this->assertEquals(2, $attributes['body']['total']); - $attributes = $attributes['body']['attributes']; - $this->assertEquals('library', $attributes[1]['relatedCollection']); + $attributes = $attributes['body']['columns']; + $this->assertEquals('library', $attributes[1]['relatedTable']); $this->assertEquals('oneToOne', $attributes[1]['relationType']); $this->assertEquals(true, $attributes[1]['twoWay']); $this->assertEquals('person', $attributes[1]['twoWayKey']); @@ -4019,8 +4019,8 @@ trait DatabasesBase ]); $this->assertEquals(1, $documents['body']['total']); - $this->assertEquals('Library 1', $documents['body']['documents'][0]['library']['libraryName']); - $this->assertArrayHasKey('fullName', $documents['body']['documents'][0]); + $this->assertEquals('Library 1', $documents['body']['rows'][0]['library']['libraryName']); + $this->assertArrayHasKey('fullName', $documents['body']['rows'][0]); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4068,7 +4068,7 @@ trait DatabasesBase $this->assertEquals(200, $attributes['headers']['status-code']); $this->assertEquals(1, $attributes['body']['total']); - $this->assertEquals('libraryName', $attributes['body']['attributes'][0]['key']); + $this->assertEquals('libraryName', $attributes['body']['columns'][0]['key']); return [ 'databaseId' => $databaseId, @@ -4107,9 +4107,9 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertIsArray($libraryAttributesResponse['body']['attributes']); + $this->assertIsArray($libraryAttributesResponse['body']['columns']); $this->assertEquals(2, $libraryAttributesResponse['body']['total']); - $this->assertEquals('person_one_to_many', $libraryAttributesResponse['body']['attributes'][1]['key']); + $this->assertEquals('person_one_to_many', $libraryAttributesResponse['body']['columns'][1]['key']); $libraryCollectionResponse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $libraryCollection, array_merge([ 'content-type' => 'application/json', @@ -4117,8 +4117,8 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertIsArray($libraryCollectionResponse['body']['attributes']); - $this->assertCount(2, $libraryCollectionResponse['body']['attributes']); + $this->assertIsArray($libraryCollectionResponse['body']['columns']); + $this->assertCount(2, $libraryCollectionResponse['body']['columns']); $attribute = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$personCollection}/attributes/libraries", array_merge([ 'content-type' => 'application/json', @@ -4541,10 +4541,10 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, count($response['body']['documents'])); - $this->assertEquals('person10', $response['body']['documents'][0]['$id']); - $this->assertEquals('Stevie Wonder', $response['body']['documents'][0]['fullName']); - $this->assertEquals(2, count($response['body']['documents'][0]['libraries'])); + $this->assertEquals(1, count($response['body']['rows'])); + $this->assertEquals('person10', $response['body']['rows'][0]['$id']); + $this->assertEquals('Stevie Wonder', $response['body']['rows'][0]['fullName']); + $this->assertEquals(2, count($response['body']['rows'][0]['libraries'])); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4558,11 +4558,11 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(2, count($response['body']['documents'])); - $this->assertEquals(null, $response['body']['documents'][0]['fullName']); - $this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]); + $this->assertEquals(2, count($response['body']['rows'])); + $this->assertEquals(null, $response['body']['rows'][0]['fullName']); + $this->assertArrayNotHasKey("libraries", $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$collectionId', $response['body']['rows'][0]); } /** @@ -4581,9 +4581,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertArrayNotHasKey('libraries', $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]); + $this->assertArrayNotHasKey('libraries', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$collectionId', $response['body']['rows'][0]); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4593,7 +4593,7 @@ trait DatabasesBase Query::select(['libraries.*', '$id'])->toString(), ], ]); - $document = $response['body']['documents'][0]; + $document = $response['body']['rows'][0]; $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('libraries', $document); $this->assertArrayNotHasKey('$databaseId', $document); @@ -4746,7 +4746,7 @@ trait DatabasesBase ); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertCount(2, $documents['body']['documents']); + $this->assertCount(2, $documents['body']['rows']); } /** diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index 2266c91afe..04daddd47a 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -226,10 +226,10 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(11, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); - $this->assertIsNumeric($response['body']['documentsTotal']); - $this->assertIsNumeric($response['body']['collectionsTotal']); - $this->assertIsArray($response['body']['collections']); - $this->assertIsArray($response['body']['documents']); + $this->assertIsNumeric($response['body']['rowsTotal']); + $this->assertIsNumeric($response['body']['tablesTotal']); + $this->assertIsArray($response['body']['tables']); + $this->assertIsArray($response['body']['rows']); } @@ -273,8 +273,8 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(3, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); - $this->assertIsNumeric($response['body']['documentsTotal']); - $this->assertIsArray($response['body']['documents']); + $this->assertIsNumeric($response['body']['rowsTotal']); + $this->assertIsArray($response['body']['rows']); } /** diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index d4a8c87868..6caeb3ca8b 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -309,11 +309,11 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); + $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); $this->assertEquals('restrict', $collection1RelationAttribute['onDelete']); } diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 57e0b93634..7e5da78ccd 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -390,12 +390,12 @@ class DatabasesCustomServerTest extends Scope ], $this->getHeaders())); $this->assertEquals(2, $collections['body']['total']); - $this->assertEquals($test1['body']['$id'], $collections['body']['collections'][0]['$id']); - $this->assertEquals($test1['body']['enabled'], $collections['body']['collections'][0]['enabled']); - $this->assertEquals($test2['body']['$id'], $collections['body']['collections'][1]['$id']); - $this->assertEquals($test1['body']['enabled'], $collections['body']['collections'][0]['enabled']); + $this->assertEquals($test1['body']['$id'], $collections['body']['tables'][0]['$id']); + $this->assertEquals($test1['body']['enabled'], $collections['body']['tables'][0]['enabled']); + $this->assertEquals($test2['body']['$id'], $collections['body']['tables'][1]['$id']); + $this->assertEquals($test1['body']['enabled'], $collections['body']['tables'][0]['enabled']); - $base = array_reverse($collections['body']['collections']); + $base = array_reverse($collections['body']['tables']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -407,7 +407,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(1, $collections['body']['collections']); + $this->assertCount(1, $collections['body']['tables']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -419,7 +419,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(1, $collections['body']['collections']); + $this->assertCount(1, $collections['body']['tables']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -431,7 +431,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(2, $collections['body']['collections']); + $this->assertCount(2, $collections['body']['tables']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -443,7 +443,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(0, $collections['body']['collections']); + $this->assertCount(0, $collections['body']['tables']); /** * Test for Order @@ -458,8 +458,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(2, $collections['body']['total']); - $this->assertEquals($base[0]['$id'], $collections['body']['collections'][0]['$id']); - $this->assertEquals($base[1]['$id'], $collections['body']['collections'][1]['$id']); + $this->assertEquals($base[0]['$id'], $collections['body']['tables'][0]['$id']); + $this->assertEquals($base[1]['$id'], $collections['body']['tables'][1]['$id']); /** * Test for After @@ -474,24 +474,24 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['collections'][0]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['tables'][0]['$id']]))->toString(), ], ]); - $this->assertCount(1, $collections['body']['collections']); - $this->assertEquals($base['body']['collections'][1]['$id'], $collections['body']['collections'][0]['$id']); + $this->assertCount(1, $collections['body']['tables']); + $this->assertEquals($base['body']['tables'][1]['$id'], $collections['body']['tables'][0]['$id']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['collections'][1]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['tables'][1]['$id']]))->toString(), ], ]); - $this->assertCount(0, $collections['body']['collections']); - $this->assertEmpty($collections['body']['collections']); + $this->assertCount(0, $collections['body']['tables']); + $this->assertEmpty($collections['body']['tables']); /** * Test for Before @@ -506,24 +506,24 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['collections'][1]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['tables'][1]['$id']]))->toString(), ], ]); - $this->assertCount(1, $collections['body']['collections']); - $this->assertEquals($base['body']['collections'][0]['$id'], $collections['body']['collections'][0]['$id']); + $this->assertCount(1, $collections['body']['tables']); + $this->assertEquals($base['body']['tables'][0]['$id'], $collections['body']['tables'][0]['$id']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['collections'][0]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['tables'][0]['$id']]))->toString(), ], ]); - $this->assertCount(0, $collections['body']['collections']); - $this->assertEmpty($collections['body']['collections']); + $this->assertCount(0, $collections['body']['tables']); + $this->assertEmpty($collections['body']['tables']); /** * Test for Search @@ -536,7 +536,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(1, $collections['body']['total']); - $this->assertEquals('first', $collections['body']['collections'][0]['$id']); + $this->assertEquals('first', $collections['body']['tables'][0]['$id']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -546,8 +546,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(2, $collections['body']['total']); - $this->assertEquals('Test 1', $collections['body']['collections'][0]['name']); - $this->assertEquals('Test 2', $collections['body']['collections'][1]['name']); + $this->assertEquals('Test 1', $collections['body']['tables'][0]['name']); + $this->assertEquals('Test 2', $collections['body']['tables'][1]['name']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -840,7 +840,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'key_lastName', 'type' => 'key', - 'attributes' => [ + 'columns' => [ 'lastName', ], ]); @@ -857,11 +857,11 @@ class DatabasesCustomServerTest extends Scope $unneededId = $unneeded['body']['key']; $this->assertEquals(200, $collection['headers']['status-code']); - $this->assertIsArray($collection['body']['attributes']); - $this->assertCount(3, $collection['body']['attributes']); - $this->assertEquals($collection['body']['attributes'][0]['key'], $firstName['body']['key']); - $this->assertEquals($collection['body']['attributes'][1]['key'], $lastName['body']['key']); - $this->assertEquals($collection['body']['attributes'][2]['key'], $unneeded['body']['key']); + $this->assertIsArray($collection['body']['columns']); + $this->assertCount(3, $collection['body']['columns']); + $this->assertEquals($collection['body']['columns'][0]['key'], $firstName['body']['key']); + $this->assertEquals($collection['body']['columns'][1]['key'], $lastName['body']['key']); + $this->assertEquals($collection['body']['columns'][2]['key'], $unneeded['body']['key']); $this->assertCount(1, $collection['body']['indexes']); $this->assertEquals($collection['body']['indexes'][0]['key'], $index['body']['key']); @@ -892,10 +892,10 @@ class DatabasesCustomServerTest extends Scope ]), []); $this->assertEquals(200, $collection['headers']['status-code']); - $this->assertIsArray($collection['body']['attributes']); - $this->assertCount(2, $collection['body']['attributes']); - $this->assertEquals($collection['body']['attributes'][0]['key'], $firstName['body']['key']); - $this->assertEquals($collection['body']['attributes'][1]['key'], $lastName['body']['key']); + $this->assertIsArray($collection['body']['columns']); + $this->assertCount(2, $collection['body']['columns']); + $this->assertEquals($collection['body']['columns'][0]['key'], $firstName['body']['key']); + $this->assertEquals($collection['body']['columns'][1]['key'], $lastName['body']['key']); return [ 'collectionId' => $actors['body']['$id'], @@ -972,7 +972,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index1', 'type' => 'key', - 'attributes' => ['attribute1', 'attribute2'], + 'columns' => ['attribute1', 'attribute2'], 'orders' => ['ASC', 'ASC'], ]); @@ -983,7 +983,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index2', 'type' => 'key', - 'attributes' => ['attribute2'], + 'columns' => ['attribute2'], ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -1015,9 +1015,9 @@ class DatabasesCustomServerTest extends Scope $this->assertIsArray($collection['body']['indexes']); $this->assertCount(1, $collection['body']['indexes']); $this->assertEquals($index1['body']['key'], $collection['body']['indexes'][0]['key']); - $this->assertIsArray($collection['body']['indexes'][0]['attributes']); - $this->assertCount(1, $collection['body']['indexes'][0]['attributes']); - $this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); + $this->assertIsArray($collection['body']['indexes'][0]['columns']); + $this->assertCount(1, $collection['body']['indexes'][0]['columns']); + $this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['columns'][0]); // Delete attribute $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/' . $attribute1['body']['key'], array_merge([ @@ -1100,7 +1100,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index1', 'type' => 'key', - 'attributes' => ['attribute1', 'attribute2'], + 'columns' => ['attribute1', 'attribute2'], 'orders' => ['ASC', 'ASC'], ]); @@ -1111,7 +1111,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index2', 'type' => 'key', - 'attributes' => ['attribute2'], + 'columns' => ['attribute2'], ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -1143,9 +1143,9 @@ class DatabasesCustomServerTest extends Scope $this->assertIsArray($collection['body']['indexes']); $this->assertCount(1, $collection['body']['indexes']); $this->assertEquals($index2['body']['key'], $collection['body']['indexes'][0]['key']); - $this->assertIsArray($collection['body']['indexes'][0]['attributes']); - $this->assertCount(1, $collection['body']['indexes'][0]['attributes']); - $this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); + $this->assertIsArray($collection['body']['indexes'][0]['columns']); + $this->assertCount(1, $collection['body']['indexes'][0]['columns']); + $this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['columns'][0]); // Delete attribute $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/' . $attribute2['body']['key'], array_merge([ @@ -1426,12 +1426,12 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $collection['headers']['status-code']); $this->assertEquals($collection['body']['name'], 'testLimitException'); - $this->assertIsArray($collection['body']['attributes']); + $this->assertIsArray($collection['body']['columns']); $this->assertIsArray($collection['body']['indexes']); - $this->assertCount(64, $collection['body']['attributes']); + $this->assertCount(64, $collection['body']['columns']); $this->assertCount(0, $collection['body']['indexes']); - foreach ($collection['body']['attributes'] as $attribute) { + foreach ($collection['body']['columns'] as $attribute) { $this->assertEquals('available', $attribute['status'], 'attribute: ' . $attribute['key']); } @@ -1446,7 +1446,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => "key_attribute{$i}", 'type' => 'key', - 'attributes' => ["attribute{$i}"], + 'columns' => ["attribute{$i}"], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -1463,9 +1463,9 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $collection['headers']['status-code']); $this->assertEquals($collection['body']['name'], 'testLimitException'); - $this->assertIsArray($collection['body']['attributes']); + $this->assertIsArray($collection['body']['columns']); $this->assertIsArray($collection['body']['indexes']); - $this->assertCount(64, $collection['body']['attributes']); + $this->assertCount(64, $collection['body']['columns']); $this->assertCount(58, $collection['body']['indexes']); $tooMany = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/indexes', array_merge([ @@ -1475,7 +1475,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'tooMany', 'type' => 'key', - 'attributes' => ['attribute61'], + 'columns' => ['attribute61'], ]); $this->assertEquals(400, $tooMany['headers']['status-code']); @@ -1681,7 +1681,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('lorem', $attribute['default']); @@ -1823,7 +1823,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('torsten@appwrite.io', $attribute['default']); @@ -1966,7 +1966,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('127.0.0.1', $attribute['default']); @@ -2108,7 +2108,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('http://appwrite.io', $attribute['default']); @@ -2254,7 +2254,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals(123, $attribute['default']); @@ -2515,7 +2515,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals(123.456, $attribute['default']); @@ -2772,7 +2772,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals(true, $attribute['default']); @@ -2914,7 +2914,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('1975-06-12 14:12:55+02:00', $attribute['default']); @@ -3061,7 +3061,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('lorem', $attribute['default']); @@ -3700,11 +3700,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); + $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -3764,8 +3764,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -3775,8 +3775,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } @@ -3810,11 +3810,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); + $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -3874,8 +3874,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -3885,8 +3885,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } @@ -3920,11 +3920,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); + $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -3984,8 +3984,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -3995,8 +3995,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } @@ -4030,11 +4030,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); + $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -4094,8 +4094,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -4105,8 +4105,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php index ca8753f374..9e20e13c2a 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php @@ -124,11 +124,11 @@ class DatabasesPermissionsGuestTest extends Scope ]); $this->assertEquals(1, $publicDocuments['body']['total']); - $this->assertEquals($permissions, $publicDocuments['body']['documents'][0]['$permissions']); + $this->assertEquals($permissions, $publicDocuments['body']['rows'][0]['$permissions']); if (\in_array(Permission::read(Role::any()), $permissions)) { $this->assertEquals(1, $privateDocuments['body']['total']); - $this->assertEquals($permissions, $privateDocuments['body']['documents'][0]['$permissions']); + $this->assertEquals($permissions, $privateDocuments['body']['rows'][0]['$permissions']); } else { $this->assertEquals(0, $privateDocuments['body']['total']); } diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php index 860fb7fb12..239fa58c99 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php @@ -186,7 +186,7 @@ class DatabasesPermissionsMemberTest extends Scope return [ 'users' => $this->users, - 'collections' => $this->collections, + 'tables' => $this->collections, 'databaseId' => $databaseId ]; } @@ -199,7 +199,7 @@ class DatabasesPermissionsMemberTest extends Scope public function testReadDocuments($permissions, $anyCount, $usersCount, $docOnlyCount, $data) { $users = $data['users']; - $collections = $data['collections']; + $collections = $data['tables']; $databaseId = $data['databaseId']; $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', $this->getServerHeader(), [ diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php index 066d83a7ee..9b0a04ac27 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php @@ -174,7 +174,7 @@ class DatabasesPermissionsTeamTest extends Scope ]); if ($success) { - $this->assertCount(1, $documents['body']['documents']); + $this->assertCount(1, $documents['body']['rows']); } else { $this->assertEquals(401, $documents['headers']['status-code']); } From 37fb34ae1207275892cf7116d0a18f840c75d4cf Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 14:33:27 +0530 Subject: [PATCH 034/173] wip: events compat based on version. --- app/controllers/shared/api.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 8c15f27acc..ba695acff4 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -483,10 +483,26 @@ App::init() /* * Background Jobs */ + $events = $route->getLabel('event', ''); $queueForEvents - ->setEvent($route->getLabel('event', '')) - ->setProject($project) - ->setUser($user); + ->setUser($user) + ->setEvent($events) + ->setProject($project); + + if (str_contains($events, '.tables.')) { + $requestFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', '')); + if ($requestFormat && version_compare($requestFormat, '1.7.0', '<')) { + $map = [ + 'rows' => 'documents', + 'tables' => 'collections', + 'columns' => 'attributes', + ]; + + $compatibleEvents = str_replace(array_keys($map), array_values($map), $events); + // override the events + $queueForEvents->setEvent($compatibleEvents); + } + } $queueForAudits ->setMode($mode) From b905af91af8f55071328e46ab59dbeb8ca63eeb0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 14:43:09 +0530 Subject: [PATCH 035/173] fix: project usage `documents` to `rows`. --- app/controllers/api/project.php | 2 +- src/Appwrite/Utopia/Response/Model/UsageProject.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 047179b888..97475c743a 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -353,7 +353,7 @@ App::get('/v1/project/usage') 'executionsTotal' => $total[METRIC_EXECUTIONS], 'executionsMbSecondsTotal' => $total[METRIC_EXECUTIONS_MB_SECONDS], 'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS], - 'documentsTotal' => $total[METRIC_DOCUMENTS], + 'rowsTotal' => $total[METRIC_DOCUMENTS], 'databasesTotal' => $total[METRIC_DATABASES], 'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE], 'usersTotal' => $total[METRIC_USERS], diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 395b19b7cd..b79cd85bed 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -16,9 +16,9 @@ class UsageProject extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('documentsTotal', [ + ->addRule('rowsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of documents.', + 'description' => 'Total aggregated number of rows.', 'default' => 0, 'example' => 0, ]) From 67a8f522e5d9ae147807a9fcd8b2c74bdfd08313 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 14:45:55 +0530 Subject: [PATCH 036/173] fix: project and general usage tests. --- tests/e2e/General/UsageTest.php | 48 +++++++++---------- .../Projects/ProjectsConsoleClientTest.php | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 5549ef800d..0c27a76311 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -416,8 +416,8 @@ class UsageTest extends Scope $requestsTotal = $data['requestsTotal']; $databasesTotal = 0; - $collectionsTotal = 0; - $documentsTotal = 0; + $tablesTotal = 0; + $rowsTotal = 0; for ($i = 0; $i < self::CREATE; $i++) { $name = uniqid() . ' database'; @@ -470,7 +470,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'collectionId' => 'unique()', + 'tableId' => 'unique()', 'name' => $name, 'documentSecurity' => false, 'permissions' => [ @@ -486,7 +486,7 @@ class UsageTest extends Scope $this->assertNotEmpty($response['body']['$id']); $requestsTotal += 1; - $collectionsTotal += 1; + $tablesTotal += 1; $collectionId = $response['body']['$id']; @@ -501,7 +501,7 @@ class UsageTest extends Scope $this->assertEmpty($response['body']); - $collectionsTotal -= 1; + $tablesTotal -= 1; $requestsTotal += 1; } } @@ -546,7 +546,7 @@ class UsageTest extends Scope $this->assertNotEmpty($response['body']['$id']); $requestsTotal += 1; - $documentsTotal += 1; + $rowsTotal += 1; $documentId = $response['body']['$id']; @@ -561,18 +561,18 @@ class UsageTest extends Scope $this->assertEmpty($response['body']); - $documentsTotal -= 1; + $rowsTotal -= 1; $requestsTotal += 1; } } return array_merge($data, [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $collectionId, 'requestsTotal' => $requestsTotal, 'databasesTotal' => $databasesTotal, - 'collectionsTotal' => $collectionsTotal, - 'documentsTotal' => $documentsTotal, + 'tablesTotal' => $tablesTotal, + 'rowsTotal' => $rowsTotal, ]); } @@ -581,11 +581,11 @@ class UsageTest extends Scope public function testDatabaseStats(array $data): array { $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $requestsTotal = $data['requestsTotal']; $databasesTotal = $data['databasesTotal']; - $collectionsTotal = $data['collectionsTotal']; - $documentsTotal = $data['documentsTotal']; + $tablesTotal = $data['tablesTotal']; + $rowsTotal = $data['rowsTotal']; sleep(self::WAIT); @@ -606,7 +606,7 @@ class UsageTest extends Scope $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); $this->validateDates($response['body']['requests']); $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); - $this->assertEquals($documentsTotal, $response['body']['documentsTotal']); + $this->assertEquals($rowsTotal, $response['body']['rowsTotal']); $response = $this->client->call( Client::METHOD_GET, @@ -616,10 +616,10 @@ class UsageTest extends Scope $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); $this->validateDates($response['body']['databases']); - $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); - $this->validateDates($response['body']['collections']); - $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); - $this->validateDates($response['body']['documents']); + $this->assertEquals($tablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); + $this->validateDates($response['body']['tables']); + $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + $this->validateDates($response['body']['rows']); $response = $this->client->call( Client::METHOD_GET, @@ -627,11 +627,11 @@ class UsageTest extends Scope $this->getConsoleHeaders() ); - $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); - $this->validateDates($response['body']['collections']); + $this->assertEquals($tablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); + $this->validateDates($response['body']['tables']); - $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); - $this->validateDates($response['body']['documents']); + $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + $this->validateDates($response['body']['rows']); $response = $this->client->call( Client::METHOD_GET, @@ -639,8 +639,8 @@ class UsageTest extends Scope $this->getConsoleHeaders() ); - $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); - $this->validateDates($response['body']['documents']); + $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + $this->validateDates($response['body']['rows']); return $data; } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index c66a581b1a..c864773535 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -483,7 +483,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertIsArray($response['body']['requests']); $this->assertIsArray($response['body']['network']); $this->assertIsNumeric($response['body']['executionsTotal']); - $this->assertIsNumeric($response['body']['documentsTotal']); + $this->assertIsNumeric($response['body']['rowsTotal']); $this->assertIsNumeric($response['body']['databasesTotal']); $this->assertIsNumeric($response['body']['bucketsTotal']); $this->assertIsNumeric($response['body']['usersTotal']); From 565e6b2ad8bfb8fb2685fad630cd5115708e0a70 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 5 May 2025 17:03:17 +0530 Subject: [PATCH 037/173] fix: migrations test <> csv imports. --- tests/e2e/Services/Migrations/MigrationsBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index c241b38e3d..208bd58731 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -1176,7 +1176,7 @@ trait MigrationsBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertIsArray($documents['body']['documents']); + $this->assertIsArray($documents['body']['rows']); $this->assertIsNumeric($documents['body']['total']); $this->assertEquals($documentsCountInCSV, $documents['body']['total']); } From 56077d59f72398369dda3771f23a3f8fb0cd3d31 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 10:51:11 +0530 Subject: [PATCH 038/173] fix: graphql tests. --- app/init/resources.php | 8 ++-- src/Appwrite/GraphQL/Types/Mapper.php | 26 ++++++------- .../Modules/Databases/Http/Tables/XList.php | 2 +- .../Request/Filters/DatabaseAliases.php | 3 +- tests/e2e/Services/GraphQL/Base.php | 38 +++++++++---------- 5 files changed, 39 insertions(+), 38 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 3a1539e59b..7657b9ea69 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -721,8 +721,8 @@ App::setResource('schema', function ($utopia, $dbForProject) { // Order must be the same as the route params return [ 'databaseId' => $databaseId, - 'documentId' => $id, - 'collectionId' => $collectionId, + 'rowId' => $id, + 'tableId' => $collectionId, 'data' => $args, 'permissions' => $permissions, ]; @@ -737,8 +737,8 @@ App::setResource('schema', function ($utopia, $dbForProject) { // Order must be the same as the route params return [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, + 'tableId' => $collectionId, + 'rowId' => $documentId, 'data' => $args, 'permissions' => $permissions, ]; diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index e5056d0abc..8cfad823af 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -418,8 +418,8 @@ class Mapper // TODO: Find a better way to do this switch ($name) { - case 'Attributes': - return static::getAttributeImplementation($object); + case 'Columns': + return static::getColumnImplementation($object); case 'HashOptions': return static::getHashOptionsImplementation($object); } @@ -427,29 +427,29 @@ class Mapper throw new Exception('Unknown union type: ' . $name); } - private static function getAttributeImplementation(array $object): Type + private static function getColumnImplementation(array $object): Type { switch ($object['type']) { case 'string': return match ($object['format'] ?? '') { - 'email' => static::model('AttributeEmail'), - 'url' => static::model('AttributeUrl'), - 'ip' => static::model('AttributeIp'), - default => static::model('AttributeString'), + 'email' => static::model('ColumnEmail'), + 'url' => static::model('ColumnUrl'), + 'ip' => static::model('ColumnIp'), + default => static::model('ColumnString'), }; case 'integer': - return static::model('AttributeInteger'); + return static::model('ColumnInteger'); case 'double': - return static::model('AttributeFloat'); + return static::model('ColumnFloat'); case 'boolean': - return static::model('AttributeBoolean'); + return static::model('ColumnBoolean'); case 'datetime': - return static::model('AttributeDatetime'); + return static::model('ColumnDatetime'); case 'relationship': - return static::model('AttributeRelationship'); + return static::model('ColumnRelationship'); } - throw new Exception('Unknown attribute implementation'); + throw new Exception('Unknown column implementation'); } private static function getHashOptionsImplementation(array $object): Type diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index 86107cc533..055102415d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -49,7 +49,7 @@ class XList extends Action responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_TABLE, + model: UtopiaResponse::MODEL_TABLE_LIST, ) ], contentType: ContentType::JSON diff --git a/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php index b2b772be11..0ee2754475 100644 --- a/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php +++ b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php @@ -12,7 +12,8 @@ class DatabaseAliases extends Filter 'attributes' => 'columns', 'collectionId' => 'tableId', 'attributeId' => 'columnId', - 'relatedCollectionId' => 'relatedTableId' + 'relatedCollection' => 'relatedTable', + 'relatedCollectionId' => 'relatedTableId', ]; public function parse(array $content, string $model): array diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 95f446cf6f..7c91cf67f7 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -259,8 +259,8 @@ trait Base // Fragments public static string $FRAGMENT_COLUMNS = ' - fragment attributeProperties on Attributes { - ... on AttributeString { + fragment columnProperties on Columns { + ... on ColumnString { key required array @@ -268,7 +268,7 @@ trait Base default size } - ... on AttributeInteger { + ... on ColumnInteger { key required array @@ -277,7 +277,7 @@ trait Base intMin: min intMax: max } - ... on AttributeFloat { + ... on ColumnFloat { key required array @@ -286,35 +286,35 @@ trait Base floatMin: min floatMax: max } - ... on AttributeBoolean { + ... on ColumnBoolean { key required array status boolDefault:default } - ... on AttributeUrl { + ... on ColumnUrl { key required array status default } - ... on AttributeEmail { + ... on ColumnEmail { key required array status default } - ... on AttributeIp { + ... on ColumnIp { key required array status default } - ... on AttributeEnum { + ... on ColumnEnum { key required array @@ -322,7 +322,7 @@ trait Base default elements } - ... on AttributeDatetime { + ... on ColumnDatetime { key required array @@ -406,7 +406,7 @@ trait Base return 'query listTables($databaseId: String!) { databasesListTables(databaseId: $databaseId) { total - collections { + tables { _id _permissions documentSecurity @@ -527,7 +527,7 @@ trait Base case self::$CREATE_RELATIONSHIP_COLUMN: return 'mutation createRelationshipColumn($databaseId: String!, $tableId: String!, $relatedTableId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ databasesCreateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, relatedTableId: $relatedTableId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { - relatedCollection + relatedTable relationType twoWay key @@ -606,7 +606,7 @@ trait Base case self::$UPDATE_RELATIONSHIP_COLUMN: return 'mutation updateRelationshipColumn($databaseId: String!, $tableId: String!, $key: String!, $onDelete: String){ databasesUpdateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, key: $key, onDelete: $onDelete) { - relatedCollection + relatedTable relationType twoWay key @@ -651,15 +651,15 @@ trait Base return 'query listColumns($databaseId: String!, $tableId: String!) { databasesListColumns(databaseId: $databaseId, tableId: $tableId) { total - attributes { - ...attributeProperties + columns { + ...columnProperties } } }' . PHP_EOL . self::$FRAGMENT_COLUMNS; case self::$GET_COLUMN: return 'query getColumn($databaseId: String!, $tableId: String!, $key: String!) { databasesGetColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { - ...attributeProperties + ...columnProperties } }' . PHP_EOL . self::$FRAGMENT_COLUMNS; case self::$DELETE_COLUMN: @@ -681,7 +681,7 @@ trait Base return 'query listRows($databaseId: String!, $tableId: String!){ databasesListRows(databaseId: $databaseId, tableId: $tableId) { total - documents { + rows { _id _collectionId _permissions @@ -2259,8 +2259,8 @@ trait Base _databaseId name documentSecurity - attributes { - ...attributeProperties + columns { + ...columnProperties } indexes { key From 9e1105b61f71b357adf5871bf57bebc9dff40061 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 11:29:56 +0530 Subject: [PATCH 039/173] update: tests. --- tests/e2e/General/AbuseTest.php | 16 +- tests/e2e/General/UsageTest.php | 2 +- .../e2e/Services/Databases/DatabasesBase.php | 152 +++++++++--------- .../Databases/DatabasesConsoleClientTest.php | 4 +- .../Databases/DatabasesCustomClientTest.php | 62 +++---- .../Databases/DatabasesCustomServerTest.php | 120 +++++++------- .../DatabasesPermissionsGuestTest.php | 18 +-- .../DatabasesPermissionsMemberTest.php | 12 +- .../DatabasesPermissionsTeamTest.php | 10 +- tests/e2e/Services/GraphQL/AbuseTest.php | 14 +- tests/e2e/Services/GraphQL/Base.php | 8 +- .../Services/Migrations/MigrationsBase.php | 14 +- .../Realtime/RealtimeConsoleClientTest.php | 4 +- .../Realtime/RealtimeCustomClientTest.php | 14 +- tests/e2e/Services/Webhooks/WebhooksBase.php | 12 +- .../Webhooks/WebhooksCustomServerTest.php | 4 +- 16 files changed, 233 insertions(+), 233 deletions(-) diff --git a/tests/e2e/General/AbuseTest.php b/tests/e2e/General/AbuseTest.php index 898fbd4aff..2ef351f43e 100644 --- a/tests/e2e/General/AbuseTest.php +++ b/tests/e2e/General/AbuseTest.php @@ -30,7 +30,7 @@ class AbuseTest extends Scope { $data = $this->createCollection(); $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $max = 120; for ($i = 0; $i <= $max + 1; $i++) { @@ -38,7 +38,7 @@ class AbuseTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'The Hulk ' . $i, ], @@ -56,7 +56,7 @@ class AbuseTest extends Scope { $data = $this->createCollection(); $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $max = 120; $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ @@ -64,7 +64,7 @@ class AbuseTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'The Hulk', ], @@ -94,7 +94,7 @@ class AbuseTest extends Scope { $data = $this->createCollection(); $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $max = 60; for ($i = 0; $i <= $max + 1; $i++) { @@ -103,7 +103,7 @@ class AbuseTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'The Hulk', ], @@ -232,7 +232,7 @@ class AbuseTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -258,7 +258,7 @@ class AbuseTest extends Scope return [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $collectionId, ]; } diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 0c27a76311..54b8597204 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -537,7 +537,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => ['name' => $name] ] ); diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 14713a52ee..3df5dd4387 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -51,7 +51,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'documentSecurity' => true, 'permissions' => [ @@ -67,7 +67,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'documentSecurity' => true, 'permissions' => [ @@ -148,7 +148,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -275,7 +275,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $data['actorsId'], + 'relatedTableId' => $data['actorsId'], 'type' => 'oneToMany', 'twoWay' => true, 'key' => 'starringActors', @@ -417,7 +417,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'patch', 'documentSecurity' => true, 'permissions' => [ @@ -488,7 +488,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Players', 'documentSecurity' => true, 'permissions' => [ @@ -540,7 +540,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Response Models', // 'permissions' missing on purpose to make sure it's optional 'documentSecurity' => true, @@ -654,7 +654,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $data['actorsId'], + 'relatedTableId' => $data['actorsId'], 'type' => 'oneToMany', 'twoWay' => true, 'key' => 'relationship', @@ -1463,7 +1463,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -1484,7 +1484,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Spider-Man: Far From Home', 'releaseYear' => 2019, @@ -1507,7 +1507,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Spider-Man: Homecoming', 'releaseYear' => 2017, @@ -1530,7 +1530,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'releaseYear' => 2020, // Missing title, expect an 400 error ], @@ -1655,7 +1655,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Dummy', 'releaseYear' => 1944, @@ -2287,7 +2287,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Thor: Ragnaroc', 'releaseYear' => 2017, @@ -2413,7 +2413,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Thor: Ragnarok', 'releaseYear' => 2017, @@ -2474,7 +2474,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'invalidDocumentStructure', 'permissions' => [ Permission::create(Role::any()), @@ -2692,7 +2692,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'email' => 'user@example.com', ], @@ -2707,7 +2707,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'enum' => 'yes', ], @@ -2722,7 +2722,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'ip' => '1.1.1.1', ], @@ -2737,7 +2737,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'url' => 'http://www.example.com', ], @@ -2752,7 +2752,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'range' => 3, ], @@ -2767,7 +2767,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'floatRange' => 1.4, ], @@ -2782,7 +2782,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'probability' => 0.99999, ], @@ -2797,7 +2797,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'upperBound' => 8, ], @@ -2812,7 +2812,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'lowerBound' => 8, ], @@ -2841,7 +2841,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'email' => 'user@@example.com', ], @@ -2856,7 +2856,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'enum' => 'badEnum', ], @@ -2871,7 +2871,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'ip' => '1.1.1.1.1', ], @@ -2886,7 +2886,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'url' => 'example...com', ], @@ -2901,7 +2901,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'range' => 11, ], @@ -2916,7 +2916,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'floatRange' => 2.5, ], @@ -2931,7 +2931,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'probability' => 1.1, ], @@ -2946,7 +2946,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'upperBound' => 11, ], @@ -2961,7 +2961,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'lowerBound' => 3, ], @@ -2976,7 +2976,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'birthDay' => '2020-10-10 27:30:10+01:00', ], @@ -3015,7 +3015,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -3145,7 +3145,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'enforceCollectionAndDocumentPermissions', 'documentSecurity' => true, 'permissions' => [ @@ -3200,7 +3200,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3217,7 +3217,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3234,7 +3234,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3338,7 +3338,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'enforceCollectionPermissions', 'permissions' => [ Permission::read(Role::user($user)), @@ -3388,7 +3388,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3405,7 +3405,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3422,7 +3422,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3541,7 +3541,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -3564,7 +3564,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America 5', 'releaseYear' => 1944, @@ -3587,7 +3587,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -3623,7 +3623,7 @@ trait DatabasesBase ]; $document = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents', $headers, [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Creation Date Test', 'releaseYear' => 2000 @@ -3691,7 +3691,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::create(Role::user(ID::custom($this->getUser()['$id']))), @@ -3728,7 +3728,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -3802,7 +3802,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Boolean' ]); @@ -3847,7 +3847,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => 'person', + 'tableId' => 'person', 'name' => 'person', 'permissions' => [ Permission::read(Role::user($this->getUser()['$id'])), @@ -3865,7 +3865,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => 'library', + 'tableId' => 'library', 'name' => 'library', 'permissions' => [ Permission::read(Role::user($this->getUser()['$id'])), @@ -3894,7 +3894,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => 'library', + 'relatedTableId' => 'library', 'type' => Database::RELATION_ONE_TO_ONE, 'key' => 'library', 'twoWay' => true, @@ -3957,7 +3957,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'library' => [ '$id' => 'library1', @@ -3981,7 +3981,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'library' => [ 'libraryName' => 'Library 2', @@ -4092,7 +4092,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => 'library', + 'relatedTableId' => 'library', 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'libraries', @@ -4141,7 +4141,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => 'person10', + 'rowId' => 'person10', 'data' => [ 'fullName' => 'Stevie Wonder', 'libraries' => [ @@ -4237,7 +4237,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Albums', 'documentSecurity' => true, 'permissions' => [ @@ -4263,7 +4263,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Artists', 'documentSecurity' => true, 'permissions' => [ @@ -4289,7 +4289,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $artists['body']['$id'], + 'relatedTableId' => $artists['body']['$id'], 'type' => Database::RELATION_MANY_TO_ONE, 'twoWay' => true, 'key' => 'artist', @@ -4318,7 +4318,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => 'album1', + 'rowId' => 'album1', 'permissions' => $permissions, 'data' => [ 'name' => 'Album 1', @@ -4381,7 +4381,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Sports', 'documentSecurity' => true, 'permissions' => [ @@ -4407,7 +4407,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Players', 'documentSecurity' => true, 'permissions' => [ @@ -4433,7 +4433,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $players['body']['$id'], + 'relatedTableId' => $players['body']['$id'], 'type' => Database::RELATION_MANY_TO_MANY, 'twoWay' => true, 'key' => 'players', @@ -4463,7 +4463,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => 'sport1', + 'rowId' => 'sport1', 'permissions' => $permissions, 'data' => [ 'name' => 'Sport 1', @@ -4641,7 +4641,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'USA Presidents', 'documentSecurity' => true, 'permissions' => [ @@ -4683,7 +4683,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'first_name' => 'Donald', 'last_name' => 'Trump', @@ -4698,7 +4698,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'first_name' => 'George', 'last_name' => 'Bush', @@ -4713,7 +4713,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'first_name' => 'Joe', 'last_name' => 'Biden', @@ -4764,7 +4764,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Collection1', 'documentSecurity' => true, 'permissions' => [ @@ -4778,7 +4778,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Collection2', 'documentSecurity' => true, 'permissions' => [ @@ -4815,7 +4815,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2, + 'relatedTableId' => $collection2, 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'collection2' @@ -4827,7 +4827,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Document 1', 'collection2' => [ @@ -4860,7 +4860,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Slow Queries', 'documentSecurity' => true, 'permissions' => [ @@ -4893,7 +4893,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'longtext' => file_get_contents(__DIR__ . '/../../../resources/longtext.txt'), ], diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index 04daddd47a..e9dca1d674 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -38,7 +38,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -69,7 +69,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TvShows', 'permissions' => [ Permission::read(Role::any()), diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index 6caeb3ca8b..b192cd04a1 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -40,7 +40,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'documentSecurity' => true, 'permissions' => [ @@ -73,7 +73,7 @@ class DatabasesCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -95,7 +95,7 @@ class DatabasesCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -138,7 +138,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('permissionCheck'), + 'tableId' => ID::custom('permissionCheck'), 'name' => 'permissionCheck', 'permissions' => [], 'documentSecurity' => true, @@ -166,7 +166,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => ID::custom('permissionCheckDocument'), + 'rowId' => ID::custom('permissionCheckDocument'), 'data' => [ 'name' => 'AppwriteBeginner', ], @@ -247,7 +247,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'level1', 'documentSecurity' => false, 'permissions' => [ @@ -264,7 +264,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'level2', 'documentSecurity' => false, 'permissions' => [ @@ -283,7 +283,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => 'oneToMany', 'twoWay' => true, 'onDelete' => 'cascade', @@ -335,7 +335,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'c1', 'documentSecurity' => false, 'permissions' => [ @@ -351,7 +351,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'c2', 'documentSecurity' => false, 'permissions' => [ @@ -369,7 +369,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_ONE, 'twoWay' => false, 'onDelete' => 'cascade', @@ -387,7 +387,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => false, 'onDelete' => 'cascade', @@ -406,7 +406,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => false, 'onDelete' => 'cascade', @@ -424,7 +424,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => false, 'onDelete' => 'cascade', @@ -442,7 +442,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => Database::RELATION_MANY_TO_MANY, 'twoWay' => true, 'onDelete' => 'setNull', @@ -461,7 +461,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => Database::RELATION_MANY_TO_MANY, 'twoWay' => true, 'onDelete' => 'setNull', @@ -495,7 +495,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection1'), + 'tableId' => ID::custom('collection1'), 'name' => ID::custom('collection1'), 'documentSecurity' => false, 'permissions' => [ @@ -511,7 +511,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection2'), + 'tableId' => ID::custom('collection2'), 'name' => ID::custom('collection2'), 'documentSecurity' => false, 'permissions' => [ @@ -524,7 +524,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection3'), + 'tableId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), 'documentSecurity' => false, 'permissions' => [ @@ -539,7 +539,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection4'), + 'tableId' => ID::custom('collection4'), 'name' => ID::custom('collection4'), 'documentSecurity' => false, 'permissions' => [ @@ -552,7 +552,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection5'), + 'tableId' => ID::custom('collection5'), 'name' => ID::custom('collection5'), 'documentSecurity' => false, 'permissions' => [ @@ -568,7 +568,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2['body']['$id'], + 'relatedTableId' => $collection2['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -581,7 +581,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection3['body']['$id'], + 'relatedTableId' => $collection3['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -594,7 +594,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection4['body']['$id'], + 'relatedTableId' => $collection4['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -607,7 +607,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection5['body']['$id'], + 'relatedTableId' => $collection5['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -681,7 +681,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => ID::custom($collection1['body']['$id']), + 'rowId' => ID::custom($collection1['body']['$id']), 'data' => [ 'Title' => 'Captain America', $collection2['body']['$id'] => [ @@ -709,7 +709,7 @@ class DatabasesCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::custom($collection1['body']['$id']), + 'rowId' => ID::custom($collection1['body']['$id']), 'data' => [ 'Title' => 'Captain America', $collection2['body']['$id'] => [ @@ -739,7 +739,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection3'), + 'tableId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), 'documentSecurity' => false, 'permissions' => [ @@ -814,7 +814,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection3'), + 'tableId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), 'documentSecurity' => false, 'permissions' => [ @@ -830,7 +830,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('collection2'), + 'tableId' => ID::custom('collection2'), 'name' => ID::custom('collection2'), 'documentSecurity' => false, 'permissions' => [ @@ -847,7 +847,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => ID::custom('collection3Doc1'), + 'rowId' => ID::custom('collection3Doc1'), 'data' => [ 'Rating' => '20' ] diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 7e5da78ccd..508578f149 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -358,7 +358,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test 1', - 'collectionId' => ID::custom('first'), + 'tableId' => ID::custom('first'), 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -374,7 +374,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test 2', - 'collectionId' => ID::custom('second'), + 'tableId' => ID::custom('second'), 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -579,7 +579,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test 1', - 'collectionId' => ID::custom('first'), + 'tableId' => ID::custom('first'), 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -592,7 +592,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(409, $response['headers']['status-code']); return [ 'databaseId' => $databaseId, - 'collectionId' => $test1['body']['$id'], + 'tableId' => $test1['body']['$id'], ]; } @@ -602,7 +602,7 @@ class DatabasesCustomServerTest extends Scope public function testGetCollection(array $data): void { $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -622,7 +622,7 @@ class DatabasesCustomServerTest extends Scope public function testUpdateCollection(array $data) { $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $collection = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -657,7 +657,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Encrypted Actors Data', 'permissions' => [ Permission::read(Role::any()), @@ -719,7 +719,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'firstName' => 'Jonah', 'lastName' => 'Jameson', @@ -767,7 +767,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -820,7 +820,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'firstName' => 'lorem', 'lastName' => 'ipsum', @@ -898,7 +898,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals($collection['body']['columns'][1]['key'], $lastName['body']['key']); return [ - 'collectionId' => $actors['body']['$id'], + 'tableId' => $actors['body']['$id'], 'key' => $index['body']['key'], 'databaseId' => $databaseId ]; @@ -910,7 +910,7 @@ class DatabasesCustomServerTest extends Scope public function testDeleteIndex($data): array { $databaseId = $data['databaseId']; - $index = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/indexes/' . $data['key'], array_merge([ + $index = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/indexes/' . $data['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -921,7 +921,7 @@ class DatabasesCustomServerTest extends Scope // Wait for database worker to finish deleting index sleep(2); - $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['collectionId'], array_merge([ + $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['tableId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -938,7 +938,7 @@ class DatabasesCustomServerTest extends Scope public function testDeleteIndexOnDeleteAttribute($data) { $databaseId = $data['databaseId']; - $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -948,7 +948,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -965,7 +965,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); - $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/indexes', array_merge([ + $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -976,7 +976,7 @@ class DatabasesCustomServerTest extends Scope 'orders' => ['ASC', 'ASC'], ]); - $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/indexes', array_merge([ + $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -994,7 +994,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); // Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/' . $attribute2['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/' . $attribute2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1005,7 +1005,7 @@ class DatabasesCustomServerTest extends Scope // wait for database worker to complete sleep(2); - $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['collectionId'], array_merge([ + $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['tableId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1020,7 +1020,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['columns'][0]); // Delete attribute - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/' . $attribute1['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/' . $attribute1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1050,7 +1050,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute', 'permissions' => [ Permission::read(Role::any()), @@ -1163,14 +1163,14 @@ class DatabasesCustomServerTest extends Scope public function testDeleteCollection($data) { $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; // Add Documents to the collection $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'firstName' => 'Tom', 'lastName' => 'Holland', @@ -1186,7 +1186,7 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'firstName' => 'Samuel', 'lastName' => 'Jackson', @@ -1249,7 +1249,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Collection1', 'documentSecurity' => false, 'permissions' => [], @@ -1260,7 +1260,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Collection2', 'documentSecurity' => false, 'permissions' => [], @@ -1274,7 +1274,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]), [ - 'relatedCollectionId' => $collection2, + 'relatedTableId' => $collection2, 'type' => Database::RELATION_MANY_TO_ONE, 'twoWay' => false, 'key' => 'collection2' @@ -1318,7 +1318,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('attributeRowWidthLimit'), + 'tableId' => ID::custom('attributeRowWidthLimit'), 'name' => 'attributeRowWidthLimit', 'permissions' => [ Permission::read(Role::any()), @@ -1384,7 +1384,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('testLimitException'), + 'tableId' => ID::custom('testLimitException'), 'name' => 'testLimitException', 'permissions' => [ Permission::read(Role::any()), @@ -1508,7 +1508,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::custom('updateAttributes'), + 'tableId' => ID::custom('updateAttributes'), 'name' => 'updateAttributes' ]); @@ -1642,7 +1642,7 @@ class DatabasesCustomServerTest extends Scope return [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId + 'tableId' => $collectionId ]; } @@ -1653,7 +1653,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'string'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string/' . $key, array_merge([ 'content-type' => 'application/json', @@ -1795,7 +1795,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'email'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/email/' . $key, array_merge([ 'content-type' => 'application/json', @@ -1938,7 +1938,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'ip'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/ip/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2080,7 +2080,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'url'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/url/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2222,7 +2222,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'integer'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2483,7 +2483,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'float'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2744,7 +2744,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'boolean'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/boolean/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2886,7 +2886,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'datetime'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/datetime/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3028,7 +3028,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'enum'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3244,7 +3244,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'string'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $document = $this->client->call( Client::METHOD_POST, @@ -3255,7 +3255,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'string' => 'string' ], @@ -3287,7 +3287,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'string' => str_repeat('a', 2048) ], @@ -3373,7 +3373,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'string' => str_repeat('a', 10) ], @@ -3408,7 +3408,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'string' => str_repeat('a', 11) ], @@ -3426,7 +3426,7 @@ class DatabasesCustomServerTest extends Scope public function testAttributeUpdateNotFound(array $data) { $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $attributes = [ 'string' => [ @@ -3514,7 +3514,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'string'; $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; // Create document to test against $document = $this->client->call( @@ -3526,7 +3526,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'string' => 'string' ], @@ -3578,7 +3578,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'new_string' => 'string' ], @@ -3600,7 +3600,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'string' => 'string' ], @@ -3630,7 +3630,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => 'collection1', + 'tableId' => 'collection1', 'name' => 'level1', 'documentSecurity' => false, 'permissions' => [ @@ -3646,7 +3646,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => 'collection2', + 'tableId' => 'collection2', 'name' => 'level2', 'documentSecurity' => false, 'permissions' => [ @@ -3684,7 +3684,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2Id, + 'relatedTableId' => $collection2Id, 'type' => 'oneToMany', 'twoWay' => true, 'onDelete' => 'cascade', @@ -3712,7 +3712,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'level2' => [[ '$id' => 'unique()', @@ -3794,7 +3794,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2Id, + 'relatedTableId' => $collection2Id, 'type' => 'oneToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -3822,7 +3822,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'level2' => [ '$id' => 'unique()', @@ -3904,7 +3904,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2Id, + 'relatedTableId' => $collection2Id, 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -3932,7 +3932,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'level2' => [ '$id' => 'unique()', @@ -4014,7 +4014,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedCollectionId' => $collection2Id, + 'relatedTableId' => $collection2Id, 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -4042,7 +4042,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'unique()', + 'rowId' => 'unique()', 'data' => [ 'level2' => [ '$id' => 'unique()', diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php index 9e20e13c2a..64413c2fab 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php @@ -32,7 +32,7 @@ class DatabasesPermissionsGuestTest extends Scope $databaseId = $database['body']['$id']; $publicMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -42,7 +42,7 @@ class DatabasesPermissionsGuestTest extends Scope ], ]); $privateMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [], 'documentSecurity' => true, @@ -94,14 +94,14 @@ class DatabasesPermissionsGuestTest extends Scope $databaseId = $data['databaseId']; $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], 'permissions' => $permissions, ]); $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -152,7 +152,7 @@ class DatabasesPermissionsGuestTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ] @@ -165,7 +165,7 @@ class DatabasesPermissionsGuestTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -175,7 +175,7 @@ class DatabasesPermissionsGuestTest extends Scope // Create a document in private collection with API key so we can test that update and delete are also not allowed $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -241,7 +241,7 @@ class DatabasesPermissionsGuestTest extends Scope $databaseId = $database['body']['$id']; $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::create(Role::any()), @@ -263,7 +263,7 @@ class DatabasesPermissionsGuestTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Thor: Ragnarok', ], diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php index 239fa58c99..2b959344fe 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php @@ -125,7 +125,7 @@ class DatabasesPermissionsMemberTest extends Scope $databaseId = $db['body']['$id']; $public = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -146,7 +146,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(202, $response['headers']['status-code']); $private = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Private Movies', 'permissions' => [ Permission::read(Role::users()), @@ -167,7 +167,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(202, $response['headers']['status-code']); $doconly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Document Only Movies', 'permissions' => [], 'documentSecurity' => true, @@ -203,7 +203,7 @@ class DatabasesPermissionsMemberTest extends Scope $databaseId = $data['databaseId']; $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -212,7 +212,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['private'] . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -221,7 +221,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['doconly'] . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php index 9b0a04ac27..5a160a25b8 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php @@ -45,7 +45,7 @@ class DatabasesPermissionsTeamTest extends Scope $this->assertEquals(201, $db['headers']['status-code']); $collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::custom('collection1'), + 'tableId' => ID::custom('collection1'), 'name' => 'Collection 1', 'permissions' => [ Permission::read(Role::team($teams['team1']['$id'])), @@ -64,7 +64,7 @@ class DatabasesPermissionsTeamTest extends Scope ]); $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections', $this->getServerHeader(), [ - 'collectionId' => ID::custom('collection2'), + 'tableId' => ID::custom('collection2'), 'name' => 'Collection 2', 'permissions' => [ Permission::read(Role::team($teams['team2']['$id'])), @@ -141,7 +141,7 @@ class DatabasesPermissionsTeamTest extends Scope $this->createCollections($this->teams); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections/' . $this->collections['collection1'] . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -149,7 +149,7 @@ class DatabasesPermissionsTeamTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections/' . $this->collections['collection2'] . '/documents', $this->getServerHeader(), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Ipsum', ], @@ -192,7 +192,7 @@ class DatabasesPermissionsTeamTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'title' => 'Ipsum', ], diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/AbuseTest.php index 7137123757..10b8421b04 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/AbuseTest.php @@ -30,7 +30,7 @@ class AbuseTest extends Scope { $data = $this->createCollection(); $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::$CREATE_ROW); $max = 120; @@ -40,8 +40,8 @@ class AbuseTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => ID::unique(), + 'tableId' => $collectionId, + 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', ], @@ -73,7 +73,7 @@ class AbuseTest extends Scope 'password' => 'password', 'databaseId' => 'database', 'databaseName' => 'database', - 'collectionId' => 'collection', + 'tableId' => 'collection', 'collectionName' => 'collection', 'collectionPermissions' => [ Permission::read(Role::users()), @@ -138,7 +138,7 @@ class AbuseTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $databaseId, - 'collectionId' => 'actors', + 'tableId' => 'actors', 'name' => 'Actors', 'documentSecurity' => false, 'permissions' => [ @@ -161,7 +161,7 @@ class AbuseTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $collectionId, 'key' => 'name', 'size' => 256, 'required' => true, @@ -178,7 +178,7 @@ class AbuseTest extends Scope return [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $collectionId, ]; } } diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 7c91cf67f7..22112d2e12 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -672,7 +672,7 @@ trait Base return 'query getRow($databaseId: String!, $tableId: String!, $rowId: String!) { databasesGetRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { _id - _collectionId + _tableId _permissions data } @@ -683,7 +683,7 @@ trait Base total rows { _id - _collectionId + _tableId _permissions data } @@ -693,7 +693,7 @@ trait Base return 'mutation createRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]){ databasesCreateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { _id - _collectionId + _tableId _permissions } }'; @@ -760,7 +760,7 @@ trait Base return 'mutation updateRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]){ databasesUpdateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { _id - _collectionId + _tableId data } }'; diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 208bd58731..a40a2156ea 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -445,7 +445,7 @@ trait MigrationsBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Test Collection', ]); @@ -536,7 +536,7 @@ trait MigrationsBase return [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $collectionId, ]; } @@ -546,14 +546,14 @@ trait MigrationsBase public function testAppwriteMigrationDatabasesDocument(array $data): void { $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; + $collectionId = $data['tableId']; $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Test Document', ] @@ -927,7 +927,7 @@ trait MigrationsBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test collection', - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -1131,7 +1131,7 @@ trait MigrationsBase return [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $collectionId, 'migrationId' => $migration['body']['$id'], ]; } @@ -1142,7 +1142,7 @@ trait MigrationsBase public function testImportSuccessful(array $response): void { $databaseId = $response['databaseId']; - $collectionId = $response['collectionId']; + $collectionId = $response['tableId']; $migrationId = $response['migrationId']; $documentsCountInCSV = 100; diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index a206692ea2..bb21ef8619 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -162,7 +162,7 @@ class RealtimeConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -272,7 +272,7 @@ class RealtimeConsoleClientTest extends Scope ], $this->getHeaders()), [ 'key' => 'key_name', 'type' => 'key', - 'attributes' => [ + 'columns' => [ 'name', ], ]); diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index d279bb45c7..671bc491f4 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -714,7 +714,7 @@ class RealtimeCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), @@ -749,7 +749,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Chris Evans' ], @@ -795,7 +795,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Chris Evans 2' ], @@ -840,7 +840,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Bradley Cooper' ], @@ -934,7 +934,7 @@ class RealtimeCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -971,7 +971,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Chris Evans' ], @@ -1053,7 +1053,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'name' => 'Bradley Cooper' ], diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index 5cd95c220d..feb43d17ab 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -61,7 +61,7 @@ trait WebhooksBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -211,7 +211,7 @@ trait WebhooksBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'firstName' => 'Chris', 'lastName' => 'Evans', @@ -254,7 +254,7 @@ trait WebhooksBase $this->assertIsArray($webhook['data']['$permissions']); $this->assertCount(3, $webhook['data']['$permissions']); - $data['documentId'] = $document['body']['$id']; + $data['rowId'] = $document['body']['$id']; return $data; } @@ -270,7 +270,7 @@ trait WebhooksBase /** * Test for SUCCESS */ - $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $data['documentId'], array_merge([ + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $data['rowId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -335,7 +335,7 @@ trait WebhooksBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'firstName' => 'Bradly', 'lastName' => 'Cooper', @@ -1119,7 +1119,7 @@ trait WebhooksBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'newCollection' . $i, 'permissions' => [ Permission::read(Role::any()), diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 14188a807b..83a29b741f 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -79,7 +79,7 @@ class WebhooksCustomServerTest extends Scope ]), [ 'key' => 'fullname', 'type' => 'key', - 'attributes' => ['lastName', 'firstName'], + 'columns' => ['lastName', 'firstName'], 'orders' => ['ASC', 'ASC'], ]); @@ -159,7 +159,7 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'Demo', 'permissions' => [ Permission::read(Role::any()), From 253c428ca226de5c777f87d56f6877ec3d42949d Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 11:30:15 +0530 Subject: [PATCH 040/173] remove: filter. --- app/controllers/general.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index f07fb2f178..6f46f529ce 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -18,7 +18,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Transformation\Adapter\Preview; use Appwrite\Transformation\Transformation; use Appwrite\Utopia\Request; -use Appwrite\Utopia\Request\Filters\DatabaseAliases; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; use Appwrite\Utopia\Request\Filters\V18 as RequestV18; @@ -835,9 +834,6 @@ App::init() } } - // process on all databases endpoints! - $request->addFilter(new DatabaseAliases()); - $domain = $request->getHostname(); $domains = Config::getParam('domains', []); if (!array_key_exists($domain, $domains)) { From 3a8f097c173c2860aeb2b7f454839cf4545c678f Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 11:31:05 +0530 Subject: [PATCH 041/173] fix: realtime channels; update: row model. --- src/Appwrite/Messaging/Adapter/Realtime.php | 4 ++-- src/Appwrite/Utopia/Response/Model/Row.php | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 400589f677..2657ecccaa 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -312,8 +312,8 @@ class Realtime extends Adapter } $channels[] = 'rows'; - $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$collectionId') . '.rows'; - $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$collectionId') . '.rows.' . $payload->getId(); + $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows'; + $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows.' . $payload->getId(); $roles = $table->getAttribute('documentSecurity', false) ? \array_merge($table->getRead(), $payload->getRead()) diff --git a/src/Appwrite/Utopia/Response/Model/Row.php b/src/Appwrite/Utopia/Response/Model/Row.php index 49325bdc81..c84a81738d 100644 --- a/src/Appwrite/Utopia/Response/Model/Row.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -36,7 +36,7 @@ class Row extends Any 'default' => '', 'example' => '5e5ea5c16897e', ]) - ->addRule('$collectionId', [ + ->addRule('$tableId', [ 'type' => self::TYPE_STRING, 'description' => 'Table ID.', 'default' => '', @@ -75,6 +75,11 @@ class Row extends Any $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); + $collectionId = $document->getAttribute('$collectionId'); + $document + ->removeAttribute('$collectionId') + ->setAttribute('$tableId', $collectionId); + foreach ($document->getAttributes() as $column) { if (\is_array($column)) { foreach ($column as $subAttribute) { From 86d195c1c0a49d7c351dac4e9dfd9be4defb9f7c Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 11:31:35 +0530 Subject: [PATCH 042/173] update: request, response filters. --- src/Appwrite/Utopia/Request/Filters/V19.php | 32 +++++++++++++++----- src/Appwrite/Utopia/Response/Filters/V19.php | 2 ++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index ee04cea038..01d2da01a2 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -6,20 +6,36 @@ use Appwrite\Utopia\Request\Filter; class V19 extends Filter { + // Map old params to new + private const PARAMS_MAP = [ + 'documentId' => 'rowId', + 'attributes' => 'columns', + 'collectionId' => 'tableId', + 'attributeId' => 'columnId', + '$collectionId' => '$tableId', + 'relatedCollection' => 'relatedTable', + 'relatedCollectionId' => 'relatedTableId', + ]; + // Convert 1.6 params to 1.7 public function parse(array $content, string $model): array { - return match ($model) { - 'databases.createRelationshipColumn' => $this->convertV16RelationshipParams($content), - default => $content - }; + $content = $this->overrideDatabaseParams($content, $model); + + return $content; } - protected function convertV16RelationshipParams(array $content): array + protected function overrideDatabaseParams(array $content, string $model): array { - if (isset($content['relatedCollectionId'])) { - $content['relatedTableId'] = $content['relatedCollectionId']; - unset($content['relatedCollectionId']); + if (!str_starts_with($model, 'databases.')) { + return $content; + } + + $intersect = array_intersect_key(self::PARAMS_MAP, $content); + + foreach ($intersect as $oldKey => $newKey) { + $content[$newKey] = $content[$oldKey]; + unset($content[$oldKey]); } return $content; diff --git a/src/Appwrite/Utopia/Response/Filters/V19.php b/src/Appwrite/Utopia/Response/Filters/V19.php index 5e1a7e40e7..24cb07889f 100644 --- a/src/Appwrite/Utopia/Response/Filters/V19.php +++ b/src/Appwrite/Utopia/Response/Filters/V19.php @@ -10,8 +10,10 @@ class V19 extends Filter private const DATABASE_MAPPINGS = [ 'table' => 'collection', 'tables' => 'collections', + '$tableId' => '$collectionId', 'tablesTotal' => 'collectionsTotal', 'relatedTable' => 'relatedCollection', + 'relatedTableId' => 'relatedCollectionId', 'column' => 'attribute', 'columns' => 'attributes', From 9ec897f7d606ca7c431655a0fadbfe9e3f14ff63 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 12:05:06 +0530 Subject: [PATCH 043/173] fix: filtering --- src/Appwrite/Utopia/Response/Model/Row.php | 10 +++--- .../e2e/Services/Databases/DatabasesBase.php | 31 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/Row.php b/src/Appwrite/Utopia/Response/Model/Row.php index c84a81738d..c30984ab7a 100644 --- a/src/Appwrite/Utopia/Response/Model/Row.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -75,10 +75,12 @@ class Row extends Any $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); - $collectionId = $document->getAttribute('$collectionId'); - $document - ->removeAttribute('$collectionId') - ->setAttribute('$tableId', $collectionId); + $collectionId = $document->getAttribute('$collectionId', ''); + if (!empty($collectionId)) { + $document + ->removeAttribute('$collectionId') + ->setAttribute('$tableId', $collectionId); + } foreach ($document->getAttributes() as $column) { if (\is_array($column)) { diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 3df5dd4387..186f906252 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1542,7 +1542,7 @@ trait DatabasesBase ]); $this->assertEquals(201, $document1['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document1['body']['$collectionId']); + $this->assertEquals($data['moviesId'], $document1['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $document1['body']); $this->assertEquals($databaseId, $document1['body']['$databaseId']); $this->assertEquals($document1['body']['title'], 'Captain America'); @@ -1555,7 +1555,7 @@ trait DatabasesBase $this->assertEquals($document1['body']['birthDay'], '1975-06-12T12:12:55.000+00:00'); $this->assertEquals(201, $document2['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document2['body']['$collectionId']); + $this->assertEquals($data['moviesId'], $document2['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $document2['body']); $this->assertEquals($databaseId, $document2['body']['$databaseId']); $this->assertEquals($document2['body']['title'], 'Spider-Man: Far From Home'); @@ -1572,7 +1572,7 @@ trait DatabasesBase $this->assertEquals($document2['body']['integers'][1], 60); $this->assertEquals(201, $document3['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document3['body']['$collectionId']); + $this->assertEquals($data['moviesId'], $document3['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $document3['body']); $this->assertEquals($databaseId, $document3['body']['$databaseId']); $this->assertEquals($document3['body']['title'], 'Spider-Man: Homecoming'); @@ -1621,7 +1621,8 @@ trait DatabasesBase $this->assertCount(3, $documents['body']['rows']); foreach ($documents['body']['rows'] as $document) { - $this->assertEquals($data['moviesId'], $document['$collectionId']); + print_r($document); + $this->assertEquals($data['moviesId'], $document['$tableId']); $this->assertArrayNotHasKey('$collection', $document); $this->assertEquals($databaseId, $document['$databaseId']); } @@ -1696,14 +1697,14 @@ trait DatabasesBase { $databaseId = $data['databaseId']; foreach ($data['rows'] as $document) { - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$tableId'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($response['body']['$id'], $document['$id']); - $this->assertEquals($document['$collectionId'], $response['body']['$collectionId']); + $this->assertEquals($document['$tableId'], $response['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $response['body']); $this->assertEquals($document['$databaseId'], $response['body']['$databaseId']); $this->assertEquals($response['body']['title'], $document['title']); @@ -1723,7 +1724,7 @@ trait DatabasesBase $databaseId = $data['databaseId']; $document = $data['rows'][0]; - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$tableId'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2305,7 +2306,7 @@ trait DatabasesBase $id = $document['body']['$id']; $this->assertEquals(201, $document['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document['body']['$collectionId']); + $this->assertEquals($data['moviesId'], $document['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $document['body']); $this->assertEquals($databaseId, $document['body']['$databaseId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnaroc'); @@ -2333,7 +2334,7 @@ trait DatabasesBase $this->assertEquals(200, $document['headers']['status-code']); $this->assertEquals($document['body']['$id'], $id); - $this->assertEquals($data['moviesId'], $document['body']['$collectionId']); + $this->assertEquals($data['moviesId'], $document['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $document['body']); $this->assertEquals($databaseId, $document['body']['$databaseId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok'); @@ -2350,7 +2351,7 @@ trait DatabasesBase $id = $document['body']['$id']; $this->assertEquals(200, $document['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document['body']['$collectionId']); + $this->assertEquals($data['moviesId'], $document['body']['$tableId']); $this->assertArrayNotHasKey('$collection', $document['body']); $this->assertEquals($databaseId, $document['body']['$databaseId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok'); @@ -4000,8 +4001,8 @@ trait DatabasesBase $this->assertEquals($databaseId, $person1['body']['$databaseId']); $this->assertEquals($databaseId, $person1['body']['library']['$databaseId']); - $this->assertEquals($person['body']['$id'], $person1['body']['$collectionId']); - $this->assertEquals($library['body']['$id'], $person1['body']['library']['$collectionId']); + $this->assertEquals($person['body']['$id'], $person1['body']['$tableId']); + $this->assertEquals($library['body']['$id'], $person1['body']['library']['$tableId']); $this->assertArrayNotHasKey('$collection', $person1['body']); $this->assertArrayNotHasKey('$collection', $person1['body']['library']); @@ -4562,7 +4563,7 @@ trait DatabasesBase $this->assertEquals(null, $response['body']['rows'][0]['fullName']); $this->assertArrayNotHasKey("libraries", $response['body']['rows'][0]); $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$collectionId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); } /** @@ -4583,7 +4584,7 @@ trait DatabasesBase $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayNotHasKey('libraries', $response['body']['rows'][0]); $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$collectionId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4597,7 +4598,7 @@ trait DatabasesBase $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('libraries', $document); $this->assertArrayNotHasKey('$databaseId', $document); - $this->assertArrayNotHasKey('$collectionId', $document); + $this->assertArrayNotHasKey('$tableId', $document); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', From b1735ef9452a9a16baaa55d4fe6c488b3149dcb5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 12:14:54 +0530 Subject: [PATCH 044/173] remove: aliasing filter. --- .../Request/Filters/DatabaseAliases.php | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php diff --git a/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php b/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php deleted file mode 100644 index 0ee2754475..0000000000 --- a/src/Appwrite/Utopia/Request/Filters/DatabaseAliases.php +++ /dev/null @@ -1,39 +0,0 @@ - 'rowId', - 'attributes' => 'columns', - 'collectionId' => 'tableId', - 'attributeId' => 'columnId', - 'relatedCollection' => 'relatedTable', - 'relatedCollectionId' => 'relatedTableId', - ]; - - public function parse(array $content, string $model): array - { - return $this->overrideDatabaseParams($content, $model); - } - - protected function overrideDatabaseParams(array $content, string $model): array - { - if (!str_starts_with($model, 'databases.')) { - return $content; - } - - $intersect = array_intersect_key(self::PARAMS_MAP, $content); - - foreach ($intersect as $oldKey => $newKey) { - $content[$newKey] = $content[$oldKey]; - unset($content[$oldKey]); - } - - return $content; - } -} From d70d001f5ab14a9d4b4437ea67d29d0797ef797a Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 12:52:05 +0530 Subject: [PATCH 045/173] add: backported channels. --- src/Appwrite/Messaging/Adapter/Realtime.php | 6 ++++++ .../Services/Realtime/RealtimeCustomClientTest.php | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 2657ecccaa..b775078c61 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -311,10 +311,16 @@ class Realtime extends Adapter throw new \Exception('Table needs to be passed to Realtime for Row events in the Database.'); } + // 1.7.x $channels[] = 'rows'; $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows'; $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows.' . $payload->getId(); + // 1.6.x + $channels[] = 'documents'; + $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$tableId') . '.documents'; + $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$tableId') . '.documents.' . $payload->getId(); + $roles = $table->getAttribute('documentSecurity', false) ? \array_merge($table->getRead(), $payload->getRead()) : $table->getRead(); diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 671bc491f4..eef7978a0e 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -769,7 +769,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); // includes old and new channels $this->assertContains('rows', $response['data']['channels']); $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows.' . $documentId, $response['data']['channels']); $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows', $response['data']['channels']); @@ -813,7 +813,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); // includes old and new channels $this->assertContains('rows', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); @@ -867,7 +867,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); // includes old and new channels $this->assertContains('rows', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); @@ -987,7 +987,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); // includes old and new channels $this->assertContains('rows', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); @@ -1026,7 +1026,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); // includes old and new channels $this->assertContains('rows', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); @@ -1076,7 +1076,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); // includes old and new channels $this->assertContains('rows', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); From 59e5ce0664e3bbe039e307acbc70c192610f68fe Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 16:44:19 +0530 Subject: [PATCH 046/173] update: `collections` <> `tables` errors. --- app/config/errors.php | 20 +++++++++---------- app/controllers/general.php | 2 +- src/Appwrite/Extend/Exception.php | 6 +++--- .../Modules/Databases/Http/Columns/Action.php | 6 +++--- .../Modules/Databases/Http/Columns/Delete.php | 4 ++-- .../Modules/Databases/Http/Columns/Get.php | 2 +- .../Http/Columns/Relationship/Create.php | 4 ++-- .../Modules/Databases/Http/Columns/XList.php | 2 +- .../Modules/Databases/Http/Indexes/Create.php | 2 +- .../Modules/Databases/Http/Indexes/Delete.php | 2 +- .../Modules/Databases/Http/Indexes/Get.php | 2 +- .../Modules/Databases/Http/Indexes/XList.php | 2 +- .../Modules/Databases/Http/Rows/Create.php | 4 ++-- .../Modules/Databases/Http/Rows/Delete.php | 4 ++-- .../Modules/Databases/Http/Rows/Get.php | 2 +- .../Databases/Http/Rows/Logs/XList.php | 2 +- .../Modules/Databases/Http/Rows/Update.php | 4 ++-- .../Modules/Databases/Http/Rows/XList.php | 2 +- .../Modules/Databases/Http/Tables/Create.php | 4 ++-- .../Modules/Databases/Http/Tables/Delete.php | 2 +- .../Modules/Databases/Http/Tables/Get.php | 2 +- .../Databases/Http/Tables/Logs/XList.php | 2 +- .../Modules/Databases/Http/Tables/Update.php | 2 +- .../Databases/Http/Tables/Usage/Get.php | 2 +- .../Databases/DatabasesCustomServerTest.php | 2 +- 25 files changed, 44 insertions(+), 44 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 9d30170c2d..6923090ee1 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -657,20 +657,20 @@ return [ 'code' => 400, ], - /** Collections */ - Exception::COLLECTION_NOT_FOUND => [ - 'name' => Exception::COLLECTION_NOT_FOUND, - 'description' => 'Collection with the requested ID could not be found.', + /** Tables */ + Exception::TABLE_NOT_FOUND => [ + 'name' => Exception::TABLE_NOT_FOUND, + 'description' => 'Table with the requested ID could not be found.', 'code' => 404, ], - Exception::COLLECTION_ALREADY_EXISTS => [ - 'name' => Exception::COLLECTION_ALREADY_EXISTS, - 'description' => 'A collection with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + Exception::TABLE_ALREADY_EXISTS => [ + 'name' => Exception::TABLE_ALREADY_EXISTS, + 'description' => 'A table with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', 'code' => 409, ], - Exception::COLLECTION_LIMIT_EXCEEDED => [ - 'name' => Exception::COLLECTION_LIMIT_EXCEEDED, - 'description' => 'The maximum number of collections has been reached.', + Exception::TABLE_LIMIT_EXCEEDED => [ + 'name' => Exception::TABLE_LIMIT_EXCEEDED, + 'description' => 'The maximum number of tables has been reached.', 'code' => 400, ], diff --git a/app/controllers/general.php b/app/controllers/general.php index 6f46f529ce..a0e707ea2f 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1185,7 +1185,7 @@ App::error() $error = new AppwriteException(AppwriteException::RELATIONSHIP_VALUE_INVALID, $error->getMessage(), previous: $error); break; case 'Utopia\Database\Exception\NotFound': - $error = new AppwriteException(AppwriteException::COLLECTION_NOT_FOUND, $error->getMessage(), previous: $error); + $error = new AppwriteException(AppwriteException::TABLE_NOT_FOUND, $error->getMessage(), previous: $error); break; case 'Utopia\Database\Exception\Dependency': $error = new AppwriteException(AppwriteException::INDEX_DEPENDENCY, null, previous: $error); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 7df9134c9f..0ea0c16174 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -192,9 +192,9 @@ class Exception extends \Exception public const DATABASE_QUERY_ORDER_NULL = 'database_query_order_null'; /** Collections */ - public const COLLECTION_NOT_FOUND = 'collection_not_found'; - public const COLLECTION_ALREADY_EXISTS = 'collection_already_exists'; - public const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded'; + public const TABLE_NOT_FOUND = 'table_not_found'; + public const TABLE_ALREADY_EXISTS = 'table_already_exists'; + public const TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; /** Documents */ public const DOCUMENT_NOT_FOUND = 'document_not_found'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php index 70c26594a3..109844534b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php @@ -78,7 +78,7 @@ class Action extends UtopiaAction $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } if (!empty($format)) { @@ -100,7 +100,7 @@ class Action extends UtopiaAction $options['side'] = Database::RELATION_SIDE_PARENT; $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); if ($relatedTable->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related table was not found.'); + throw new Exception(Exception::TABLE_NOT_FOUND, 'The related table was not found.'); } } @@ -229,7 +229,7 @@ class Action extends UtopiaAction $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php index 945097e66b..c4c681b080 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php @@ -81,7 +81,7 @@ class Delete extends Action $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); @@ -109,7 +109,7 @@ class Delete extends Action if ($options['twoWay']) { $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); if ($relatedTable->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php index 160ab60868..091b778eb8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php @@ -75,7 +75,7 @@ class Get extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index d063c8e055..9628d79a92 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -105,13 +105,13 @@ class Create extends ColumnAction $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $relatedTableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId); $relatedTable = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedTableDocument->getInternalId()); if ($relatedTable->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $columns = $table->getAttribute('attributes', []); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php index e9800e71a0..2b212e2be3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -68,7 +68,7 @@ class XList extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $queries = Query::parseQueries($queries); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php index 49cb7157b0..1b45f58a2f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php @@ -85,7 +85,7 @@ class Create extends Action $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $count = $dbForProject->count('indexes', [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php index eeb772e0c3..78e1799164 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php @@ -74,7 +74,7 @@ class Delete extends Action $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php index 72ab1a02b1..b1689481ed 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php @@ -67,7 +67,7 @@ class Get extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $index = $table->find('key', $key, 'indexes'); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php index 4920b679c1..53c623f9f4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php @@ -73,7 +73,7 @@ class XList extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $queries = Query::parseQueries($queries); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php index 55efbc3632..adf481c1c4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -106,7 +106,7 @@ class Create extends Action $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $allowedPermissions = [ @@ -246,7 +246,7 @@ class Create extends Action } catch (DuplicateException) { throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } catch (NotFoundException) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php index bba042bca5..16e16b7d73 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php @@ -84,7 +84,7 @@ class Delete extends Action $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } // Read permission should not be required for delete @@ -102,7 +102,7 @@ class Delete extends Action ); }); } catch (NotFoundException) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } // Add $tableId and $databaseId for all rows diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php index 85b6127bd3..f2da5e69f4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -79,7 +79,7 @@ class Get extends Action $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php index 02358be8f2..43e61c1434 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php @@ -81,7 +81,7 @@ class XList extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index c9ed71d569..6de7e6ae92 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -101,7 +101,7 @@ class Update extends Action $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } // Read permission should not be required for update @@ -243,7 +243,7 @@ class Update extends Action } catch (StructureException $e) { throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); } catch (NotFoundException) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } // Add $tableId and $databaseId for all rows diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php index 4396c2b112..95410507bf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -79,7 +79,7 @@ class XList extends Action $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php index 9acd248190..dc3f7b8574 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php @@ -99,9 +99,9 @@ class Create extends Action $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); } catch (DuplicateException) { - throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); + throw new Exception(Exception::TABLE_ALREADY_EXISTS); } catch (LimitException) { - throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); + throw new Exception(Exception::TABLE_LIMIT_EXCEEDED); } $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php index 6fe7228ed9..b7e8f6ecf7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -71,7 +71,7 @@ class Delete extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $tableId)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php index 63bc1fa48c..4c76c1f89d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php @@ -66,7 +66,7 @@ class Get extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $response->dynamic($table, UtopiaResponse::MODEL_TABLE); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php index 44a850424f..1dba2493cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php @@ -81,7 +81,7 @@ class XList extends Action $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php index d5f8d388ab..f5621feab3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -77,7 +77,7 @@ class Update extends Action $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $permissions ??= $table->getPermissions() ?? []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php index 4c591bff1a..b528fee7cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -68,7 +68,7 @@ class Get extends Action $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); if ($table->isEmpty()) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::TABLE_NOT_FOUND); } $periods = Config::getParam('usage', []); diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 508578f149..a5beb77bf3 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -3491,7 +3491,7 @@ class DatabasesCustomServerTest extends Scope ]), $payload); $this->assertEquals(404, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLLECTION_NOT_FOUND, $update['body']['type']); + $this->assertEquals(AppwriteException::TABLE_NOT_FOUND, $update['body']['type']); /** * Check if Attribute exists From 6e8dbfc6749bd44a53bbe4ad07dc8c27087f5554 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 16:48:04 +0530 Subject: [PATCH 047/173] update: `documents` <> `rows` errors. --- app/config/errors.php | 44 +++++++++---------- app/controllers/general.php | 8 ++-- src/Appwrite/Extend/Exception.php | 16 +++---- .../Modules/Databases/Http/Rows/Create.php | 8 ++-- .../Modules/Databases/Http/Rows/Delete.php | 2 +- .../Modules/Databases/Http/Rows/Get.php | 2 +- .../Databases/Http/Rows/Logs/XList.php | 2 +- .../Modules/Databases/Http/Rows/Update.php | 8 ++-- .../e2e/Services/Databases/DatabasesBase.php | 2 +- .../Databases/DatabasesCustomServerTest.php | 2 +- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 6923090ee1..12d494faf6 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -674,40 +674,40 @@ return [ 'code' => 400, ], - /** Documents */ - Exception::DOCUMENT_NOT_FOUND => [ - 'name' => Exception::DOCUMENT_NOT_FOUND, - 'description' => 'Document with the requested ID could not be found.', + /** Rows */ + Exception::ROW_NOT_FOUND => [ + 'name' => Exception::ROW_NOT_FOUND, + 'description' => 'Row with the requested ID could not be found.', 'code' => 404, ], - Exception::DOCUMENT_INVALID_STRUCTURE => [ - 'name' => Exception::DOCUMENT_INVALID_STRUCTURE, - 'description' => 'The document structure is invalid. Please ensure the attributes match the collection definition.', + Exception::ROW_INVALID_STRUCTURE => [ + 'name' => Exception::ROW_INVALID_STRUCTURE, + 'description' => 'The row structure is invalid. Please ensure the columns match the table definition.', 'code' => 400, ], - Exception::DOCUMENT_MISSING_DATA => [ - 'name' => Exception::DOCUMENT_MISSING_DATA, - 'description' => 'The document data is missing. Try again with document data populated', + Exception::ROW_MISSING_DATA => [ + 'name' => Exception::ROW_MISSING_DATA, + 'description' => 'The row data is missing. Try again with row data populated', 'code' => 400, ], - Exception::DOCUMENT_MISSING_PAYLOAD => [ - 'name' => Exception::DOCUMENT_MISSING_PAYLOAD, - 'description' => 'The document data and permissions are missing. You must provide either document data or permissions to be updated.', + Exception::ROW_MISSING_PAYLOAD => [ + 'name' => Exception::ROW_MISSING_PAYLOAD, + 'description' => 'The row data and permissions are missing. You must provide either row data or permissions to be updated.', 'code' => 400, ], - Exception::DOCUMENT_ALREADY_EXISTS => [ - 'name' => Exception::DOCUMENT_ALREADY_EXISTS, - 'description' => 'Document with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + Exception::ROW_ALREADY_EXISTS => [ + 'name' => Exception::ROW_ALREADY_EXISTS, + 'description' => 'Row with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', 'code' => 409, ], - Exception::DOCUMENT_UPDATE_CONFLICT => [ - 'name' => Exception::DOCUMENT_UPDATE_CONFLICT, - 'description' => 'Remote document is newer than local.', + Exception::ROW_UPDATE_CONFLICT => [ + 'name' => Exception::ROW_UPDATE_CONFLICT, + 'description' => 'Remote row is newer than local.', 'code' => 409, ], - Exception::DOCUMENT_DELETE_RESTRICTED => [ - 'name' => Exception::DOCUMENT_DELETE_RESTRICTED, - 'description' => 'Document cannot be deleted because it is referenced by another document.', + Exception::ROW_DELETE_RESTRICTED => [ + 'name' => Exception::ROW_DELETE_RESTRICTED, + 'description' => 'Row cannot be deleted because it is referenced by another document.', 'code' => 403, ], diff --git a/app/controllers/general.php b/app/controllers/general.php index a0e707ea2f..cc6739e790 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1161,7 +1161,7 @@ App::error() } break; case 'Utopia\Database\Exception\Conflict': - $error = new AppwriteException(AppwriteException::DOCUMENT_UPDATE_CONFLICT, previous: $error); + $error = new AppwriteException(AppwriteException::ROW_UPDATE_CONFLICT, previous: $error); break; case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); @@ -1170,13 +1170,13 @@ App::error() $error = new AppwriteException(AppwriteException::GENERAL_QUERY_INVALID, $error->getMessage(), previous: $error); break; case 'Utopia\Database\Exception\Structure': - $error = new AppwriteException(AppwriteException::DOCUMENT_INVALID_STRUCTURE, $error->getMessage(), previous: $error); + $error = new AppwriteException(AppwriteException::ROW_INVALID_STRUCTURE, $error->getMessage(), previous: $error); break; case 'Utopia\Database\Exception\Duplicate': - $error = new AppwriteException(AppwriteException::DOCUMENT_ALREADY_EXISTS); + $error = new AppwriteException(AppwriteException::ROW_ALREADY_EXISTS); break; case 'Utopia\Database\Exception\Restricted': - $error = new AppwriteException(AppwriteException::DOCUMENT_DELETE_RESTRICTED); + $error = new AppwriteException(AppwriteException::ROW_DELETE_RESTRICTED); break; case 'Utopia\Database\Exception\Authorization': $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 0ea0c16174..6468a774ce 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -196,14 +196,14 @@ class Exception extends \Exception public const TABLE_ALREADY_EXISTS = 'table_already_exists'; public const TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; - /** Documents */ - public const DOCUMENT_NOT_FOUND = 'document_not_found'; - public const DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure'; - public const DOCUMENT_MISSING_DATA = 'document_missing_data'; - public const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload'; - public const DOCUMENT_ALREADY_EXISTS = 'document_already_exists'; - public const DOCUMENT_UPDATE_CONFLICT = 'document_update_conflict'; - public const DOCUMENT_DELETE_RESTRICTED = 'document_delete_restricted'; + /** Rows */ + public const ROW_NOT_FOUND = 'row_not_found'; + public const ROW_INVALID_STRUCTURE = 'row_invalid_structure'; + public const ROW_MISSING_DATA = 'row_missing_data'; + public const ROW_MISSING_PAYLOAD = 'row_missing_payload'; + public const ROW_ALREADY_EXISTS = 'row_already_exists'; + public const ROW_UPDATE_CONFLICT = 'row_update_conflict'; + public const ROW_DELETE_RESTRICTED = 'row_delete_restricted'; /** Attribute */ public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php index adf481c1c4..df36696d80 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -87,11 +87,11 @@ class Create extends Action $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data)) { - throw new Exception(Exception::DOCUMENT_MISSING_DATA); + throw new Exception(Exception::ROW_MISSING_DATA); } if (isset($data['$id'])) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new rows, try update instead'); + throw new Exception(Exception::ROW_INVALID_STRUCTURE, '$id is not allowed for creating new rows, try update instead'); } $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -242,9 +242,9 @@ class Create extends Action try { $row = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $row); } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + throw new Exception(Exception::ROW_INVALID_STRUCTURE, $e->getMessage()); } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::ROW_ALREADY_EXISTS); } catch (NotFoundException) { throw new Exception(Exception::TABLE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php index 16e16b7d73..8c68f23bfb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php @@ -91,7 +91,7 @@ class Delete extends Action $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::ROW_NOT_FOUND); } try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php index f2da5e69f4..69127ca238 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -92,7 +92,7 @@ class Get extends Action } if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::ROW_NOT_FOUND); } $operations = 0; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php index 43e61c1434..238815d849 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php @@ -87,7 +87,7 @@ class XList extends Action $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId); if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::ROW_NOT_FOUND); } try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index 6de7e6ae92..7dcd1f8fcb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -86,7 +86,7 @@ class Update extends Action $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && \is_null($permissions)) { - throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); + throw new Exception(Exception::ROW_MISSING_PAYLOAD); } $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -109,7 +109,7 @@ class Update extends Action $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); if ($row->isEmpty()) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::ROW_NOT_FOUND); } // Map aggregate permissions into the multiple permissions they represent. @@ -239,9 +239,9 @@ class Update extends Action } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::ROW_ALREADY_EXISTS); } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + throw new Exception(Exception::ROW_INVALID_STRUCTURE, $e->getMessage()); } catch (NotFoundException) { throw new Exception(Exception::TABLE_NOT_FOUND); } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 186f906252..3c2a2c4186 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -2399,7 +2399,7 @@ trait DatabasesBase $this->assertEquals(409, $response['headers']['status-code']); $this->assertEquals('Remote document is newer than local.', $response['body']['message']); - $this->assertEquals(Exception::DOCUMENT_UPDATE_CONFLICT, $response['body']['type']); + $this->assertEquals(Exception::ROW_UPDATE_CONFLICT, $response['body']['type']); return []; } diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index a5beb77bf3..314e3e1ae1 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -3417,7 +3417,7 @@ class DatabasesCustomServerTest extends Scope ); $this->assertEquals(400, $newDoc['headers']['status-code']); - $this->assertEquals(AppwriteException::DOCUMENT_INVALID_STRUCTURE, $newDoc['body']['type']); + $this->assertEquals(AppwriteException::ROW_INVALID_STRUCTURE, $newDoc['body']['type']); } /** From 7a52ace9b25c952d7d1b801ad0e76dde3f80c159 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 16:53:20 +0530 Subject: [PATCH 048/173] update: `attributes` <> `columns` errors. --- app/config/errors.php | 66 +++++++++---------- src/Appwrite/Extend/Exception.php | 22 +++---- .../Modules/Databases/Http/Columns/Action.php | 44 ++++++------- .../Modules/Databases/Http/Columns/Delete.php | 4 +- .../Databases/Http/Columns/Enum/Create.php | 2 +- .../Databases/Http/Columns/Float/Create.php | 4 +- .../Modules/Databases/Http/Columns/Get.php | 2 +- .../Databases/Http/Columns/Integer/Create.php | 4 +- .../Http/Columns/Relationship/Create.php | 6 +- .../Databases/Http/Columns/String/Create.php | 2 +- .../Modules/Databases/Http/Indexes/Create.php | 6 +- .../Databases/DatabasesCustomServerTest.php | 38 +++++------ 12 files changed, 100 insertions(+), 100 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 12d494faf6..979bf43f7d 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -71,7 +71,7 @@ return [ ], Exception::GENERAL_QUERY_LIMIT_EXCEEDED => [ 'name' => Exception::GENERAL_QUERY_LIMIT_EXCEEDED, - 'description' => 'Query limit exceeded for the current attribute. Usage of more than 100 query values on a single attribute is prohibited.', + 'description' => 'Query limit exceeded for the current column. Usage of more than 100 query values on a single column is prohibited.', 'code' => 400, ], Exception::GENERAL_QUERY_INVALID => [ @@ -653,7 +653,7 @@ return [ ], Exception::DATABASE_QUERY_ORDER_NULL => [ 'name' => Exception::DATABASE_QUERY_ORDER_NULL, - 'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.', + 'description' => 'The order column had a null value. Cursor pagination requires all rows order column values are non-null.', 'code' => 400, ], @@ -707,54 +707,54 @@ return [ ], Exception::ROW_DELETE_RESTRICTED => [ 'name' => Exception::ROW_DELETE_RESTRICTED, - 'description' => 'Row cannot be deleted because it is referenced by another document.', + 'description' => 'Row cannot be deleted because it is referenced by another row.', 'code' => 403, ], - /** Attributes */ - Exception::ATTRIBUTE_NOT_FOUND => [ - 'name' => Exception::ATTRIBUTE_NOT_FOUND, - 'description' => 'Attribute with the requested ID could not be found.', + /** Columns */ + Exception::COLUMN_NOT_FOUND => [ + 'name' => Exception::COLUMN_NOT_FOUND, + 'description' => 'Column with the requested ID could not be found.', 'code' => 404, ], - Exception::ATTRIBUTE_UNKNOWN => [ - 'name' => Exception::ATTRIBUTE_UNKNOWN, - 'description' => 'The attribute required for the index could not be found. Please confirm all your attributes are in the available state.', + Exception::COLUMN_UNKNOWN => [ + 'name' => Exception::COLUMN_UNKNOWN, + 'description' => 'The column required for the index could not be found. Please confirm all your columns are in the available state.', 'code' => 400, ], - Exception::ATTRIBUTE_NOT_AVAILABLE => [ - 'name' => Exception::ATTRIBUTE_NOT_AVAILABLE, - 'description' => 'The requested attribute is not yet available. Please try again later.', + Exception::COLUMN_NOT_AVAILABLE => [ + 'name' => Exception::COLUMN_NOT_AVAILABLE, + 'description' => 'The requested column is not yet available. Please try again later.', 'code' => 400, ], - Exception::ATTRIBUTE_FORMAT_UNSUPPORTED => [ - 'name' => Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, - 'description' => 'The requested attribute format is not supported.', + Exception::COLUMN_FORMAT_UNSUPPORTED => [ + 'name' => Exception::COLUMN_FORMAT_UNSUPPORTED, + 'description' => 'The requested column format is not supported.', 'code' => 400, ], - Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED => [ - 'name' => Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, - 'description' => 'Default values cannot be set for array or required attributes.', + Exception::COLUMN_DEFAULT_UNSUPPORTED => [ + 'name' => Exception::COLUMN_DEFAULT_UNSUPPORTED, + 'description' => 'Default values cannot be set for array or required columns.', 'code' => 400, ], - Exception::ATTRIBUTE_ALREADY_EXISTS => [ - 'name' => Exception::ATTRIBUTE_ALREADY_EXISTS, - 'description' => 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', + Exception::COLUMN_ALREADY_EXISTS => [ + 'name' => Exception::COLUMN_ALREADY_EXISTS, + 'description' => 'Column with the requested key already exists. Column keys must be unique, try again with a different key.', 'code' => 409, ], - Exception::ATTRIBUTE_LIMIT_EXCEEDED => [ - 'name' => Exception::ATTRIBUTE_LIMIT_EXCEEDED, - 'description' => 'The maximum number or size of attributes for this collection has been reached.', + Exception::COLUMN_LIMIT_EXCEEDED => [ + 'name' => Exception::COLUMN_LIMIT_EXCEEDED, + 'description' => 'The maximum number or size of columns for this table has been reached.', 'code' => 400, ], - Exception::ATTRIBUTE_VALUE_INVALID => [ - 'name' => Exception::ATTRIBUTE_VALUE_INVALID, - 'description' => 'The attribute value is invalid. Please check the type, range and value of the attribute.', + Exception::COLUMN_VALUE_INVALID => [ + 'name' => Exception::COLUMN_VALUE_INVALID, + 'description' => 'The column value is invalid. Please check the type, range and value of the column.', 'code' => 400, ], - Exception::ATTRIBUTE_TYPE_INVALID => [ - 'name' => Exception::ATTRIBUTE_TYPE_INVALID, - 'description' => 'The attribute type is invalid.', + Exception::COLUMN_TYPE_INVALID => [ + 'name' => Exception::COLUMN_TYPE_INVALID, + 'description' => 'The column type is invalid.', 'code' => 400, ], Exception::RELATIONSHIP_VALUE_INVALID => [ @@ -762,8 +762,8 @@ return [ 'description' => 'The relationship value is invalid.', 'code' => 400, ], - Exception::ATTRIBUTE_INVALID_RESIZE => [ - 'name' => Exception::ATTRIBUTE_INVALID_RESIZE, + Exception::COLUMN_INVALID_RESIZE => [ + 'name' => Exception::COLUMN_INVALID_RESIZE, 'description' => "Existing data is too large for new size, truncate your existing data then try again.", 'code' => 400, ], diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 6468a774ce..31a52a6a04 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -205,17 +205,17 @@ class Exception extends \Exception public const ROW_UPDATE_CONFLICT = 'row_update_conflict'; public const ROW_DELETE_RESTRICTED = 'row_delete_restricted'; - /** Attribute */ - public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found'; - public const ATTRIBUTE_UNKNOWN = 'attribute_unknown'; - public const ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available'; - public const ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported'; - public const ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported'; - public const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists'; - public const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded'; - public const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid'; - public const ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid'; - public const ATTRIBUTE_INVALID_RESIZE = 'attribute_invalid_resize'; + /** Columns */ + public const COLUMN_NOT_FOUND = 'column_not_found'; + public const COLUMN_UNKNOWN = 'column_unknown'; + public const COLUMN_NOT_AVAILABLE = 'column_not_available'; + public const COLUMN_FORMAT_UNSUPPORTED = 'column_format_unsupported'; + public const COLUMN_DEFAULT_UNSUPPORTED = 'column_default_unsupported'; + public const COLUMN_ALREADY_EXISTS = 'column_already_exists'; + public const COLUMN_LIMIT_EXCEEDED = 'column_limit_exceeded'; + public const COLUMN_VALUE_INVALID = 'column_value_invalid'; + public const COLUMN_TYPE_INVALID = 'column_type_invalid'; + public const COLUMN_INVALID_RESIZE = 'column_invalid_resize'; /** Relationship */ public const RELATIONSHIP_VALUE_INVALID = 'relationship_value_invalid'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php index 109844534b..a9415fded3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php @@ -83,17 +83,17 @@ class Action extends UtopiaAction if (!empty($format)) { if (!Structure::hasFormat($format, $type)) { - throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns."); + throw new Exception(Exception::COLUMN_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns."); } } // Must throw here since dbForProject->createAttribute is performed by db worker if ($required && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); + throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); } if ($array && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); + throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); } if ($type === Database::VAR_RELATIONSHIP) { @@ -128,9 +128,9 @@ class Action extends UtopiaAction $dbForProject->checkAttribute($table, $column); $column = $dbForProject->createDocument('attributes', $column); } catch (DuplicateException) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); + throw new Exception(Exception::COLUMN_ALREADY_EXISTS); } catch (LimitException) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + throw new Exception(Exception::COLUMN_LIMIT_EXCEEDED); } catch (Throwable $e) { $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); @@ -171,10 +171,10 @@ class Action extends UtopiaAction $dbForProject->createDocument('attributes', $twoWayAttribute); } catch (DuplicateException) { $dbForProject->deleteDocument('attributes', $column->getId()); - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); + throw new Exception(Exception::COLUMN_ALREADY_EXISTS); } catch (LimitException) { $dbForProject->deleteDocument('attributes', $column->getId()); - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + throw new Exception(Exception::COLUMN_LIMIT_EXCEEDED); } catch (Throwable $e) { $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); @@ -235,27 +235,27 @@ class Action extends UtopiaAction $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); if ($column->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::COLUMN_NOT_FOUND); } if ($column->getAttribute('status') !== 'available') { - throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE); + throw new Exception(Exception::COLUMN_NOT_AVAILABLE); } if ($column->getAttribute(('type') !== $type)) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); + throw new Exception(Exception::COLUMN_TYPE_INVALID); } if ($column->getAttribute('type') === Database::VAR_STRING && $column->getAttribute(('filter') !== $filter)) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); + throw new Exception(Exception::COLUMN_TYPE_INVALID); } if ($required && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); + throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); } if ($column->getAttribute('array', false) && isset($default)) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); + throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); } $tableId = 'database_' . $db->getInternalId() . '_collection_' . $table->getInternalId(); @@ -275,7 +275,7 @@ class Action extends UtopiaAction $max ??= $column->getAttribute('formatOptions')['max']; if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } if ($column->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { @@ -289,7 +289,7 @@ class Action extends UtopiaAction } if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); } $options = [ @@ -301,17 +301,17 @@ class Action extends UtopiaAction break; case APP_DATABASE_ATTRIBUTE_ENUM: if (empty($elements)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Enum elements must not be empty'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Enum elements must not be empty'); } foreach ($elements as $element) { if (\strlen($element) === 0) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Each enum element must not be empty'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Each enum element must not be empty'); } } if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Default value not found in elements'); } $options = [ @@ -334,7 +334,7 @@ class Action extends UtopiaAction onDelete: $primaryRowOptions['onDelete'], ); } catch (NotFoundException) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::COLUMN_NOT_FOUND); } if ($primaryRowOptions['twoWay']) { @@ -364,11 +364,11 @@ class Action extends UtopiaAction newKey: $newKey ?? null ); } catch (TruncateException) { - throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE); + throw new Exception(Exception::COLUMN_INVALID_RESIZE); } catch (NotFoundException) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::COLUMN_NOT_FOUND); } catch (LimitException) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + throw new Exception(Exception::COLUMN_LIMIT_EXCEEDED); } catch (IndexException $e) { throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php index c4c681b080..717e8f958c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php @@ -86,7 +86,7 @@ class Delete extends Action $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); if ($column->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::COLUMN_NOT_FOUND); } $validator = new IndexDependencyValidator( @@ -114,7 +114,7 @@ class Delete extends Action $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); if ($relatedColumn->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::COLUMN_NOT_FOUND); } if ($relatedColumn->getAttribute('status') === 'available') { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php index 5c5c7da071..c57d451743 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php @@ -84,7 +84,7 @@ class Create extends ColumnAction Event $queueForEvents ): void { if (!is_null($default) && !in_array($default, $elements, true)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Default value not found in elements'); } $column = $this->createColumn( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index 150ce8d25e..a0a16fafec 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -89,12 +89,12 @@ class Create extends ColumnAction $max ??= PHP_FLOAT_MAX; if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } $validator = new Range($min, $max, Database::VAR_FLOAT); if (!\is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); } $column = $this->createColumn($databaseId, $tableId, new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php index 091b778eb8..4390f0841c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php @@ -80,7 +80,7 @@ class Get extends Action $column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key); if ($column->isEmpty()) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::COLUMN_NOT_FOUND); } $type = $column->getAttribute('type'); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php index d58b79d597..cf2244bf1f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php @@ -89,12 +89,12 @@ class Create extends ColumnAction $max ??= \PHP_INT_MAX; if ($min > $max) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); + throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } $validator = new Range($min, $max, Database::VAR_INTEGER); if (!\is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); } $size = $max > 2147483647 ? 8 : 4; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index 9628d79a92..8b3ec616a3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -121,14 +121,14 @@ class Create extends ColumnAction } if (\strtolower($column->getId()) === \strtolower($key)) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); + throw new Exception(Exception::COLUMN_ALREADY_EXISTS); } if ( \strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() ) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); + throw new Exception(Exception::COLUMN_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); } if ( @@ -136,7 +136,7 @@ class Create extends ColumnAction $column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() ) { - throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same table is currently not permitted.'); + throw new Exception(Exception::COLUMN_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same table is currently not permitted.'); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php index caafdbc32f..e768fda1d6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php @@ -89,7 +89,7 @@ class Create extends ColumnAction // Ensure default fits in the given size $validator = new Text($size, 0); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); + throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); } $filters = []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php index 1b45f58a2f..537d8e27f0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php @@ -142,7 +142,7 @@ class Create extends Action $columnIndex = \array_search($column, array_column($oldColumns, 'key')); if ($columnIndex === false) { - throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown column: ' . $column . '. Verify the column name or create the column.'); + throw new Exception(Exception::COLUMN_UNKNOWN, 'Unknown column: ' . $column . '. Verify the column name or create the column.'); } $columnStatus = $oldColumns[$columnIndex]['status']; @@ -150,12 +150,12 @@ class Create extends Action $columnArray = $oldColumns[$columnIndex]['array'] ?? false; if ($columnType === Database::VAR_RELATIONSHIP) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Cannot create an index for a relationship column: ' . $oldColumns[$columnIndex]['key']); + throw new Exception(Exception::COLUMN_TYPE_INVALID, 'Cannot create an index for a relationship column: ' . $oldColumns[$columnIndex]['key']); } // ensure attribute is available if ($columnStatus !== 'available') { - throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Column not available: ' . $oldColumns[$columnIndex]['key']); + throw new Exception(Exception::COLUMN_NOT_AVAILABLE, 'Column not available: ' . $oldColumns[$columnIndex]['key']); } $lengths[$i] = null; diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 314e3e1ae1..874aa5623e 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -1785,7 +1785,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -1928,7 +1928,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -2070,7 +2070,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -2212,7 +2212,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -2430,7 +2430,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2444,7 +2444,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2458,7 +2458,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ @@ -2473,7 +2473,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); } /** @@ -2691,7 +2691,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2705,7 +2705,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2719,7 +2719,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ @@ -2734,7 +2734,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); } /** @@ -2876,7 +2876,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -3018,7 +3018,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -3133,7 +3133,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3146,7 +3146,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3234,7 +3234,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -3324,7 +3324,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $attribute['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_INVALID_RESIZE, $attribute['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_INVALID_RESIZE, $attribute['body']['type']); // original documents to original size, remove new document $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document['body']['$id'], array_merge([ @@ -3503,7 +3503,7 @@ class DatabasesCustomServerTest extends Scope ]), $payload); $this->assertEquals(404, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::ATTRIBUTE_NOT_FOUND, $update['body']['type']); + $this->assertEquals(AppwriteException::COLUMN_NOT_FOUND, $update['body']['type']); } } From 6eb1c3ae686ad9c549522a70aef5aa587103194c Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 16:58:56 +0530 Subject: [PATCH 049/173] update: rename remaining variables. --- .../Platform/Modules/Databases/Http/Rows/Create.php | 8 ++++---- .../Platform/Modules/Databases/Http/Rows/Delete.php | 6 +++--- src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php | 2 +- .../Platform/Modules/Databases/Http/Rows/Update.php | 8 ++++---- .../Platform/Modules/Databases/Http/Rows/XList.php | 2 +- .../Platform/Modules/Databases/Workers/Databases.php | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php index df36696d80..87078a5d3d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -175,7 +175,7 @@ class Create extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -257,7 +257,7 @@ class Create extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -296,10 +296,10 @@ class Create extends Action ->dynamic($row, UtopiaResponse::MODEL_ROW); $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), + fn ($row) => $row->getAttribute('key'), \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php index 8c68f23bfb..61cd4d218a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php @@ -112,7 +112,7 @@ class Delete extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -147,10 +147,10 @@ class Delete extends Action $response->addHeader('X-Debug-Operations', 1); $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), + fn ($row) => $row->getAttribute('key'), \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php index 69127ca238..fb600f5a7f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -110,7 +110,7 @@ class Get extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index 7dcd1f8fcb..5590aa6c67 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -156,7 +156,7 @@ class Update extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -253,7 +253,7 @@ class Update extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -284,10 +284,10 @@ class Update extends Action $response->dynamic($row, UtopiaResponse::MODEL_ROW); $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), + fn ($row) => $row->getAttribute('key'), \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php index 95410507bf..781c0d7c83 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -136,7 +136,7 @@ class XList extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { diff --git a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 6564239630..37fa7cf0cc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -539,9 +539,9 @@ class Databases extends Action Query::contains('options', ['"relatedCollection":"'. $collectionId .'"']), ], $dbForProject, - function ($attribute) use ($dbForProject, $databaseInternalId) { - $dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $attribute->getAttribute('collectionId')); - $dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $attribute->getAttribute('collectionInternalId')); + function ($column) use ($dbForProject, $databaseInternalId) { + $dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $column->getAttribute('collectionId')); + $dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $column->getAttribute('collectionInternalId')); } ); From 3e813f6cba68f057bbb70318f49b0708530f7370 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 17:00:12 +0530 Subject: [PATCH 050/173] update: change filter name for collections <> tables. --- src/Appwrite/GraphQL/Types/Mapper.php | 2 +- src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php | 4 ++-- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- .../Validator/Queries/{Collections.php => Tables.php} | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/Appwrite/Utopia/Database/Validator/Queries/{Collections.php => Tables.php} (91%) diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index 8cfad823af..e763c3b42c 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -284,7 +284,7 @@ class Mapper case 'Utopia\Database\Validator\Authorization': case 'Appwrite\Utopia\Database\Validator\Queries\Base': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': - case 'Appwrite\Utopia\Database\Validator\Queries\Collections': + case 'Appwrite\Utopia\Database\Validator\Queries\Tables': case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': case 'Appwrite\Utopia\Database\Validator\Queries\Indexes': case 'Appwrite\Utopia\Database\Validator\Queries\Databases': diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index 055102415d..e6d2a2d02a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -7,7 +7,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; -use Appwrite\Utopia\Database\Validator\Queries\Collections; +use Appwrite\Utopia\Database\Validator\Queries\Tables; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -55,7 +55,7 @@ class XList extends Action contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('queries', [], new Collections(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Collections::ALLOWED_ATTRIBUTES), true) + ->param('queries', [], new Tables(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Tables::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 4452f63f0f..9497110950 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -409,7 +409,7 @@ class OpenAPI3 extends Format break; case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': - case 'Appwrite\Utopia\Database\Validator\Queries\Collections': + case 'Appwrite\Utopia\Database\Validator\Queries\Tables': case 'Appwrite\Utopia\Database\Validator\Queries\Databases': case 'Appwrite\Utopia\Database\Validator\Queries\Deployments': case 'Appwrite\Utopia\Database\Validator\Queries\Executions': diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Collections.php b/src/Appwrite/Utopia/Database/Validator/Queries/Tables.php similarity index 91% rename from src/Appwrite/Utopia/Database/Validator/Queries/Collections.php rename to src/Appwrite/Utopia/Database/Validator/Queries/Tables.php index 6e8fcb8339..5a1fc4ec6d 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Collections.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Tables.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; -class Collections extends Base +class Tables extends Base { public const ALLOWED_ATTRIBUTES = [ 'name', From ed9306ef8d1a56101548d08c59247d9bbf642dd2 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 6 May 2025 17:00:57 +0530 Subject: [PATCH 051/173] update: change filter name for attributes <> columns. --- src/Appwrite/GraphQL/Types/Mapper.php | 2 +- .../Platform/Modules/Databases/Http/Columns/XList.php | 4 ++-- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- .../Validator/Queries/{Attributes.php => Columns.php} | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/Appwrite/Utopia/Database/Validator/Queries/{Attributes.php => Columns.php} (93%) diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index e763c3b42c..45aa9e0085 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -285,7 +285,7 @@ class Mapper case 'Appwrite\Utopia\Database\Validator\Queries\Base': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': case 'Appwrite\Utopia\Database\Validator\Queries\Tables': - case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': + case 'Appwrite\Utopia\Database\Validator\Queries\Columns': case 'Appwrite\Utopia\Database\Validator\Queries\Indexes': case 'Appwrite\Utopia\Database\Validator\Queries\Databases': case 'Appwrite\Utopia\Database\Validator\Queries\Deployments': diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php index e9800e71a0..b23fafcbd0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -6,7 +6,7 @@ use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; -use Appwrite\Utopia\Database\Validator\Queries\Attributes; +use Appwrite\Utopia\Database\Validator\Queries\Columns; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -53,7 +53,7 @@ class XList extends Action )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') - ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) + ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Columns::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') ->callback([$this, 'action']); diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 9497110950..088a20ff98 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -407,7 +407,7 @@ class OpenAPI3 extends Format 'type' => $validator->getValidator()->getType(), ]; break; - case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': + case 'Appwrite\Utopia\Database\Validator\Queries\Columns': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': case 'Appwrite\Utopia\Database\Validator\Queries\Tables': case 'Appwrite\Utopia\Database\Validator\Queries\Databases': diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php b/src/Appwrite/Utopia/Database/Validator/Queries/Columns.php similarity index 93% rename from src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php rename to src/Appwrite/Utopia/Database/Validator/Queries/Columns.php index 4a35c82b73..3c2742bfdd 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Columns.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; -class Attributes extends Base +class Columns extends Base { public const ALLOWED_ATTRIBUTES = [ 'key', From ee117b816a7ecc4c2bbf4a2f7245825dc7907c7c Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 10:33:54 +0530 Subject: [PATCH 052/173] manage: events and channels. --- app/controllers/shared/api.php | 22 +++------------------ src/Appwrite/Event/Realtime.php | 6 ++++-- src/Appwrite/Messaging/Adapter/Realtime.php | 14 ++++++------- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index ba695acff4..8c15f27acc 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -483,26 +483,10 @@ App::init() /* * Background Jobs */ - $events = $route->getLabel('event', ''); $queueForEvents - ->setUser($user) - ->setEvent($events) - ->setProject($project); - - if (str_contains($events, '.tables.')) { - $requestFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', '')); - if ($requestFormat && version_compare($requestFormat, '1.7.0', '<')) { - $map = [ - 'rows' => 'documents', - 'tables' => 'collections', - 'columns' => 'attributes', - ]; - - $compatibleEvents = str_replace(array_keys($map), array_values($map), $events); - // override the events - $queueForEvents->setEvent($compatibleEvents); - } - } + ->setEvent($route->getLabel('event', '')) + ->setProject($project) + ->setUser($user); $queueForAudits ->setMode($mode) diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index e1ec891e67..67d51e5c52 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -76,16 +76,18 @@ class Realtime extends Event $payload = new Document($this->getPayload()); $db = $this->getContext('database'); - $table = $this->getContext('table'); $bucket = $this->getContext('bucket'); + // can be Tables API or Collections API, generated channels include both! + $tableOrCollection = $this->getContext('table') ?? $this->getContext('collection'); + $target = RealtimeAdapter::fromPayload( // Pass first, most verbose event pattern event: $allEvents[0], payload: $payload, project: $this->getProject(), database: $db, - table: $table, + table: $tableOrCollection, bucket: $bucket, ); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index b775078c61..5cb908464f 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -303,23 +303,23 @@ class Realtime extends Adapter $channels[] = 'projects.' . $project->getId(); $projectId = 'console'; $roles = [Role::team($project->getAttribute('teamId'))->toString()]; - } elseif (($parts[4] ?? '') === 'rows') { + } elseif (($parts[4] ?? '') === 'rows' || ($parts[4] ?? '') === 'documents') { if ($database->isEmpty()) { - throw new \Exception('Database needs to be passed to Realtime for Row events in the Database.'); + throw new \Exception('Database needs to be passed to Realtime for Document/Row events in the Database.'); } if ($table->isEmpty()) { - throw new \Exception('Table needs to be passed to Realtime for Row events in the Database.'); + throw new \Exception('Collection or the Table needs to be passed to Realtime for Document/Row events in the Database.'); } - // 1.7.x + // 1.7.x - Tables API $channels[] = 'rows'; $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows'; $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows.' . $payload->getId(); - // 1.6.x + // 1.6.x - Collections API $channels[] = 'documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$tableId') . '.documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$tableId') . '.documents.' . $payload->getId(); + $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents'; + $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId(); $roles = $table->getAttribute('documentSecurity', false) ? \array_merge($table->getRead(), $payload->getRead()) From 46ccf6f5370dd7c7a871fed143a257e2736c84d1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 11:13:15 +0530 Subject: [PATCH 053/173] update: re-add legacy models; remove: filters. --- app/config/events.php | 50 ++++++++ app/controllers/api/project.php | 2 +- app/init/resources.php | 9 +- src/Appwrite/GraphQL/Types/Mapper.php | 43 ++++--- .../Specification/Format/OpenAPI3.php | 2 + .../Database/Validator/Queries/Attributes.php | 25 ++++ .../Validator/Queries/Collections.php | 21 ++++ src/Appwrite/Utopia/Request/Filters/V19.php | 35 ++---- src/Appwrite/Utopia/Response.php | 53 +++++++++ src/Appwrite/Utopia/Response/Filters/V19.php | 77 +------------ .../Utopia/Response/Model/Attribute.php | 85 ++++++++++++++ .../Response/Model/AttributeBoolean.php | 59 ++++++++++ .../Response/Model/AttributeDatetime.php | 67 +++++++++++ .../Utopia/Response/Model/AttributeEmail.php | 66 +++++++++++ .../Utopia/Response/Model/AttributeEnum.php | 73 ++++++++++++ .../Utopia/Response/Model/AttributeFloat.php | 73 ++++++++++++ .../Utopia/Response/Model/AttributeIP.php | 66 +++++++++++ .../Response/Model/AttributeInteger.php | 72 ++++++++++++ .../Utopia/Response/Model/AttributeList.php | 58 ++++++++++ .../Response/Model/AttributeRelationship.php | 96 +++++++++++++++ .../Utopia/Response/Model/AttributeString.php | 53 +++++++++ .../Utopia/Response/Model/AttributeURL.php | 66 +++++++++++ .../Utopia/Response/Model/BaseList.php | 8 +- .../Utopia/Response/Model/Collection.php | 109 ++++++++++++++++++ .../Utopia/Response/Model/Document.php | 92 +++++++++++++++ .../Utopia/Response/Model/UsageCollection.php | 54 +++++++++ 26 files changed, 1283 insertions(+), 131 deletions(-) create mode 100644 src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php create mode 100644 src/Appwrite/Utopia/Database/Validator/Queries/Collections.php create mode 100644 src/Appwrite/Utopia/Response/Model/Attribute.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeBoolean.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeDatetime.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeEmail.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeEnum.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeFloat.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeIP.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeInteger.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeList.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeRelationship.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeString.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeURL.php create mode 100644 src/Appwrite/Utopia/Response/Model/Collection.php create mode 100644 src/Appwrite/Utopia/Response/Model/Document.php create mode 100644 src/Appwrite/Utopia/Response/Model/UsageCollection.php diff --git a/app/config/events.php b/app/config/events.php index 4523b56206..182aa91363 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -145,6 +145,56 @@ return [ '$description' => 'This event triggers when a table is updated.', ] ], + 'collections' => [ + '$model' => Response::MODEL_COLLECTION, + '$resource' => true, + '$description' => 'This event triggers on any collection event.', + 'documents' => [ + '$model' => Response::MODEL_DOCUMENT, + '$resource' => true, + '$description' => 'This event triggers on any document event.', + 'create' => [ + '$description' => 'This event triggers when a document is created.', + ], + 'delete' => [ + '$description' => 'This event triggers when a document is deleted.' + ], + 'update' => [ + '$description' => 'This event triggers when a document is updated.' + ], + ], + 'indexes' => [ + '$model' => Response::MODEL_INDEX, + '$resource' => true, + '$description' => 'This event triggers on any indexes event.', + 'create' => [ + '$description' => 'This event triggers when an index is created.', + ], + 'delete' => [ + '$description' => 'This event triggers when an index is deleted.' + ] + ], + 'attributes' => [ + '$model' => Response::MODEL_ATTRIBUTE, + '$resource' => true, + '$description' => 'This event triggers on any attributes event.', + 'create' => [ + '$description' => 'This event triggers when an attribute is created.', + ], + 'delete' => [ + '$description' => 'This event triggers when an attribute is deleted.' + ] + ], + 'create' => [ + '$description' => 'This event triggers when a collection is created.' + ], + 'delete' => [ + '$description' => 'This event triggers when a collection is deleted.', + ], + 'update' => [ + '$description' => 'This event triggers when a collection is updated.', + ] + ], 'create' => [ '$description' => 'This event triggers when a database is created.' ], diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 97475c743a..047179b888 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -353,7 +353,7 @@ App::get('/v1/project/usage') 'executionsTotal' => $total[METRIC_EXECUTIONS], 'executionsMbSecondsTotal' => $total[METRIC_EXECUTIONS_MB_SECONDS], 'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS], - 'rowsTotal' => $total[METRIC_DOCUMENTS], + 'documentsTotal' => $total[METRIC_DOCUMENTS], 'databasesTotal' => $total[METRIC_DATABASES], 'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE], 'usersTotal' => $total[METRIC_USERS], diff --git a/app/init/resources.php b/app/init/resources.php index 7657b9ea69..5ac26281a2 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -707,6 +707,7 @@ App::setResource('schema', function ($utopia, $dbForProject) { }, ]; + // NOTE: `params` and `urls` are not used internally in the `Schema::build` function below! $params = [ 'list' => function (string $databaseId, string $collectionId, array $args) { return [ 'queries' => $args['queries']]; @@ -721,8 +722,8 @@ App::setResource('schema', function ($utopia, $dbForProject) { // Order must be the same as the route params return [ 'databaseId' => $databaseId, - 'rowId' => $id, - 'tableId' => $collectionId, + 'documentId' => $id, + 'collectionId' => $collectionId, 'data' => $args, 'permissions' => $permissions, ]; @@ -737,8 +738,8 @@ App::setResource('schema', function ($utopia, $dbForProject) { // Order must be the same as the route params return [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, - 'rowId' => $documentId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, 'data' => $args, 'permissions' => $permissions, ]; diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index 45aa9e0085..a56b7f2f8a 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -285,7 +285,9 @@ class Mapper case 'Appwrite\Utopia\Database\Validator\Queries\Base': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': case 'Appwrite\Utopia\Database\Validator\Queries\Tables': + case 'Appwrite\Utopia\Database\Validator\Queries\Collections': case 'Appwrite\Utopia\Database\Validator\Queries\Columns': + case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': case 'Appwrite\Utopia\Database\Validator\Queries\Indexes': case 'Appwrite\Utopia\Database\Validator\Queries\Databases': case 'Appwrite\Utopia\Database\Validator\Queries\Deployments': @@ -418,8 +420,10 @@ class Mapper // TODO: Find a better way to do this switch ($name) { - case 'Columns': + case 'Attributes': return static::getColumnImplementation($object); + case 'Columns': + return static::getColumnImplementation($object, true); case 'HashOptions': return static::getHashOptionsImplementation($object); } @@ -427,29 +431,24 @@ class Mapper throw new Exception('Unknown union type: ' . $name); } - private static function getColumnImplementation(array $object): Type + private static function getColumnImplementation(array $object, bool $isColumns = false): Type { - switch ($object['type']) { - case 'string': - return match ($object['format'] ?? '') { - 'email' => static::model('ColumnEmail'), - 'url' => static::model('ColumnUrl'), - 'ip' => static::model('ColumnIp'), - default => static::model('ColumnString'), - }; - case 'integer': - return static::model('ColumnInteger'); - case 'double': - return static::model('ColumnFloat'); - case 'boolean': - return static::model('ColumnBoolean'); - case 'datetime': - return static::model('ColumnDatetime'); - case 'relationship': - return static::model('ColumnRelationship'); - } + $prefix = $isColumns ? 'Column' : 'Attribute'; - throw new Exception('Unknown column implementation'); + return match ($object['type']) { + 'string' => match ($object['format'] ?? '') { + 'email' => static::model("{$prefix}Email"), + 'url' => static::model("{$prefix}Url"), + 'ip' => static::model("{$prefix}Ip"), + default => static::model("{$prefix}String"), + }, + 'integer' => static::model("{$prefix}Integer"), + 'double' => static::model("{$prefix}Float"), + 'boolean' => static::model("{$prefix}Boolean"), + 'datetime' => static::model("{$prefix}Datetime"), + 'relationship' => static::model("{$prefix}Relationship"), + default => throw new Exception('Unknown ' . strtolower($prefix) . ' implementation'), + }; } private static function getHashOptionsImplementation(array $object): Type diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 088a20ff98..9c906d3ebc 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -408,8 +408,10 @@ class OpenAPI3 extends Format ]; break; case 'Appwrite\Utopia\Database\Validator\Queries\Columns': + case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': case 'Appwrite\Utopia\Database\Validator\Queries\Tables': + case 'Appwrite\Utopia\Database\Validator\Queries\Collections': case 'Appwrite\Utopia\Database\Validator\Queries\Databases': case 'Appwrite\Utopia\Database\Validator\Queries\Deployments': case 'Appwrite\Utopia\Database\Validator\Queries\Executions': diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php b/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php new file mode 100644 index 0000000000..4a35c82b73 --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php @@ -0,0 +1,25 @@ + 'rowId', - 'attributes' => 'columns', - 'collectionId' => 'tableId', - 'attributeId' => 'columnId', - '$collectionId' => '$tableId', - 'relatedCollection' => 'relatedTable', - 'relatedCollectionId' => 'relatedTableId', - ]; - // Convert 1.6 params to 1.7 public function parse(array $content, string $model): array { - $content = $this->overrideDatabaseParams($content, $model); - - return $content; - } - - protected function overrideDatabaseParams(array $content, string $model): array - { - if (!str_starts_with($model, 'databases.')) { - return $content; - } - - $intersect = array_intersect_key(self::PARAMS_MAP, $content); - - foreach ($intersect as $oldKey => $newKey) { - $content[$newKey] = $content[$oldKey]; - unset($content[$oldKey]); + /* + Uncomment with first request filter; current is just a copy of V18 + switch ($model) { + case 'functions.create': + $content['something'] = $content['somethingElse'] ?? ""; + unset($content['something']); + break; } + */ return $content; } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 62822e9956..c72a01c79a 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -15,10 +15,23 @@ use Appwrite\Utopia\Response\Model\AlgoScrypt; use Appwrite\Utopia\Response\Model\AlgoScryptModified; use Appwrite\Utopia\Response\Model\AlgoSha; use Appwrite\Utopia\Response\Model\Any; +use Appwrite\Utopia\Response\Model\Attribute; +use Appwrite\Utopia\Response\Model\AttributeBoolean; +use Appwrite\Utopia\Response\Model\AttributeDatetime; +use Appwrite\Utopia\Response\Model\AttributeEmail; +use Appwrite\Utopia\Response\Model\AttributeEnum; +use Appwrite\Utopia\Response\Model\AttributeFloat; +use Appwrite\Utopia\Response\Model\AttributeInteger; +use Appwrite\Utopia\Response\Model\AttributeIP; +use Appwrite\Utopia\Response\Model\AttributeList; +use Appwrite\Utopia\Response\Model\AttributeRelationship; +use Appwrite\Utopia\Response\Model\AttributeString; +use Appwrite\Utopia\Response\Model\AttributeURL; use Appwrite\Utopia\Response\Model\AuthProvider; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Branch; use Appwrite\Utopia\Response\Model\Bucket; +use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\Column; use Appwrite\Utopia\Response\Model\ColumnBoolean; use Appwrite\Utopia\Response\Model\ColumnDatetime; @@ -40,6 +53,7 @@ use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Utopia\Response\Model\DetectionFramework; use Appwrite\Utopia\Response\Model\DetectionRuntime; use Appwrite\Utopia\Response\Model\DevKey; +use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; use Appwrite\Utopia\Response\Model\Execution; @@ -106,6 +120,7 @@ use Appwrite\Utopia\Response\Model\TemplateVariable; use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Topic; use Appwrite\Utopia\Response\Model\UsageBuckets; +use Appwrite\Utopia\Response\Model\UsageCollection; use Appwrite\Utopia\Response\Model\UsageDatabase; use Appwrite\Utopia\Response\Model\UsageDatabases; use Appwrite\Utopia\Response\Model\UsageFunction; @@ -148,6 +163,7 @@ class Response extends SwooleResponse public const MODEL_USAGE_DATABASES = 'usageDatabases'; public const MODEL_USAGE_DATABASE = 'usageDatabase'; public const MODEL_USAGE_TABLE = 'usageTable'; + public const MODEL_USAGE_COLLECTION = 'usageCollection'; public const MODEL_USAGE_USERS = 'usageUsers'; public const MODEL_USAGE_BUCKETS = 'usageBuckets'; public const MODEL_USAGE_STORAGE = 'usageStorage'; @@ -160,14 +176,32 @@ class Response extends SwooleResponse // Database public const MODEL_DATABASE = 'database'; public const MODEL_DATABASE_LIST = 'databaseList'; + public const MODEL_COLLECTION = 'collection'; + public const MODEL_COLLECTION_LIST = 'collectionList'; public const MODEL_TABLE = 'table'; public const MODEL_TABLE_LIST = 'tableList'; public const MODEL_INDEX = 'index'; public const MODEL_INDEX_LIST = 'indexList'; + public const MODEL_DOCUMENT = 'document'; + public const MODEL_DOCUMENT_LIST = 'documentList'; public const MODEL_ROW = 'row'; public const MODEL_ROW_LIST = 'rowList'; // Database Attributes + public const MODEL_ATTRIBUTE = 'attribute'; + public const MODEL_ATTRIBUTE_LIST = 'attributeList'; + public const MODEL_ATTRIBUTE_STRING = 'attributeString'; + public const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger'; + public const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat'; + public const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean'; + public const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail'; + public const MODEL_ATTRIBUTE_ENUM = 'attributeEnum'; + public const MODEL_ATTRIBUTE_IP = 'attributeIp'; + public const MODEL_ATTRIBUTE_URL = 'attributeUrl'; + public const MODEL_ATTRIBUTE_DATETIME = 'attributeDatetime'; + public const MODEL_ATTRIBUTE_RELATIONSHIP = 'attributeRelationship'; + + // Database Columns public const MODEL_COLUMN = 'column'; public const MODEL_COLUMN_LIST = 'columnList'; public const MODEL_COLUMN_STRING = 'columnString'; @@ -377,7 +411,9 @@ class Response extends SwooleResponse ->setModel(new ErrorDev()) // Lists ->setModel(new BaseList('Rows List', self::MODEL_ROW_LIST, 'rows', self::MODEL_ROW)) + ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) ->setModel(new BaseList('Tables List', self::MODEL_TABLE_LIST, 'tables', self::MODEL_TABLE)) + ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) @@ -428,6 +464,21 @@ class Response extends SwooleResponse ->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT)) // Entities ->setModel(new Database()) + // Collection API Models + ->setModel(new Collection()) + ->setModel(new Attribute()) + ->setModel(new AttributeList()) + ->setModel(new AttributeString()) + ->setModel(new AttributeInteger()) + ->setModel(new AttributeFloat()) + ->setModel(new AttributeBoolean()) + ->setModel(new AttributeEmail()) + ->setModel(new AttributeEnum()) + ->setModel(new AttributeIP()) + ->setModel(new AttributeURL()) + ->setModel(new AttributeDatetime()) + ->setModel(new AttributeRelationship()) + // Table API Models ->setModel(new Table()) ->setModel(new Column()) ->setModel(new ColumnList()) @@ -443,6 +494,7 @@ class Response extends SwooleResponse ->setModel(new ColumnRelationship()) ->setModel(new Index()) ->setModel(new Row()) + ->setModel(new ModelDocument()) ->setModel(new Log()) ->setModel(new User()) ->setModel(new AlgoMd5()) @@ -509,6 +561,7 @@ class Response extends SwooleResponse ->setModel(new UsageDatabases()) ->setModel(new UsageDatabase()) ->setModel(new UsageTable()) + ->setModel(new UsageCollection()) ->setModel(new UsageUsers()) ->setModel(new UsageStorage()) ->setModel(new UsageBuckets()) diff --git a/src/Appwrite/Utopia/Response/Filters/V19.php b/src/Appwrite/Utopia/Response/Filters/V19.php index 24cb07889f..4505b2c8f2 100644 --- a/src/Appwrite/Utopia/Response/Filters/V19.php +++ b/src/Appwrite/Utopia/Response/Filters/V19.php @@ -7,44 +7,18 @@ use Appwrite\Utopia\Response\Filter; class V19 extends Filter { - private const DATABASE_MAPPINGS = [ - 'table' => 'collection', - 'tables' => 'collections', - '$tableId' => '$collectionId', - 'tablesTotal' => 'collectionsTotal', - 'relatedTable' => 'relatedCollection', - 'relatedTableId' => 'relatedCollectionId', - - 'column' => 'attribute', - 'columns' => 'attributes', - 'columnsTotal' => 'attributesTotal', - - 'row' => 'document', - 'rows' => 'documents', - 'rowsTotal' => 'documentsTotal' - ]; - // Convert 1.7 Data format to 1.6 format public function parse(array $content, string $model): array { $parsedResponse = $content; - return match ($model) { - Response::MODEL_ROW, - Response::MODEL_TABLE, - Response::MODEL_COLUMN, - Response::MODEL_ROW_LIST, - Response::MODEL_TABLE_LIST, - Response::MODEL_COLUMN_LIST, - Response::MODEL_USAGE_TABLE, - Response::MODEL_USAGE_DATABASE, - Response::MODEL_USAGE_DATABASES, - Response::MODEL_COLUMN_RELATIONSHIP => $this->handleDBTerminology($model, $content), - + $parsedResponse = match($model) { Response::MODEL_FUNCTION => $this->parseFunction($content), Response::MODEL_FUNCTION_LIST => $this->handleList($content, 'functions', fn ($item) => $this->parseFunction($item)), default => $parsedResponse, }; + + return $parsedResponse; } protected function parseFunction(array $content): array @@ -53,49 +27,4 @@ class V19 extends Filter unset($content['deploymentId']); return $content; } - - protected function handleDBTerminology(string $model, array $content): array - { - $isListModel = match ($model) { - Response::MODEL_ROW_LIST, - Response::MODEL_TABLE_LIST, - Response::MODEL_COLUMN_LIST => true, - - default => false - }; - - if ($isListModel) { - foreach (self::DATABASE_MAPPINGS as $oldKey => $newKey) { - if (isset($content[$oldKey])) { - $content[$newKey] = array_map(fn ($item) => $this->remapKeys($item), $content[$oldKey]); - unset($content[$oldKey]); - } - } - } else { - $content = $this->remapKeysRecursive($content); - } - - return $content; - } - - private function remapKeys(array $data): array - { - foreach (self::DATABASE_MAPPINGS as $old => $new) { - if (isset($data[$old])) { - $data[$new] = $data[$old]; - unset($data[$old]); - } - } - return $data; - } - - private function remapKeysRecursive(array $data): array - { - $result = []; - foreach ($data as $key => $value) { - $newKey = self::DATABASE_MAPPINGS[$key] ?? $key; - $result[$newKey] = \is_array($value) ? $this->remapKeysRecursive($value) : $value; - } - return $result; - } } diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php new file mode 100644 index 0000000000..8c43f8d21c --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -0,0 +1,85 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', + 'default' => '', + 'example' => 'available', + ]) + ->addRule('error', [ + 'type' => self::TYPE_STRING, + 'description' => 'Error message. Displays error generated on failure of creating or deleting an attribute.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'required' => false, + 'example' => false, + ]) + ->addRule('$createdAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Attribute creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$updatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Attribute update date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]); + } + + public array $conditions = []; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'Attribute'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php new file mode 100644 index 0000000000..05846817ca --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -0,0 +1,59 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'isEnabled', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'boolean', + ]) + ->addRule('default', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => false + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_BOOLEAN + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeBoolean'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_BOOLEAN; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeDatetime.php b/src/Appwrite/Utopia/Response/Model/AttributeDatetime.php new file mode 100644 index 0000000000..4651aebd06 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeDatetime.php @@ -0,0 +1,67 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'birthDay', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => self::TYPE_DATETIME, + ]) + ->addRule('format', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'ISO 8601 format.', + 'default' => APP_DATABASE_ATTRIBUTE_DATETIME, + 'example' => APP_DATABASE_ATTRIBUTE_DATETIME, + 'array' => false, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Only null is optional', + 'default' => null, + 'example' => self::TYPE_DATETIME_EXAMPLE, + 'array' => false, + 'required' => false, + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_DATETIME + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeDatetime'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_DATETIME; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php new file mode 100644 index 0000000000..078087dd4b --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -0,0 +1,66 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'userEmail', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => APP_DATABASE_ATTRIBUTE_EMAIL, + 'example' => APP_DATABASE_ATTRIBUTE_EMAIL, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => 'default@example.com', + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_EMAIL + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeEmail'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_EMAIL; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEnum.php b/src/Appwrite/Utopia/Response/Model/AttributeEnum.php new file mode 100644 index 0000000000..992b62ee3a --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeEnum.php @@ -0,0 +1,73 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'status', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('elements', [ + 'type' => self::TYPE_STRING, + 'description' => 'Array of elements in enumerated type.', + 'default' => null, + 'example' => 'element', + 'array' => true, + ]) + ->addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => APP_DATABASE_ATTRIBUTE_ENUM, + 'example' => APP_DATABASE_ATTRIBUTE_ENUM, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => 'element', + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_ENUM + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeEnum'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_ENUM; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php new file mode 100644 index 0000000000..266b89c330 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -0,0 +1,73 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'percentageCompleted', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'double', + ]) + ->addRule('min', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Minimum value to enforce for new documents.', + 'default' => null, + 'required' => false, + 'example' => 1.5, + ]) + ->addRule('max', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Maximum value to enforce for new documents.', + 'default' => null, + 'required' => false, + 'example' => 10.5, + ]) + ->addRule('default', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => 2.5, + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_FLOAT, + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeFloat'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_FLOAT; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php new file mode 100644 index 0000000000..cfa309317a --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -0,0 +1,66 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'ipAddress', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => APP_DATABASE_ATTRIBUTE_IP, + 'example' => APP_DATABASE_ATTRIBUTE_IP, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => '192.0.2.0', + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_IP + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeIP'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_IP; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php new file mode 100644 index 0000000000..fddfe57445 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -0,0 +1,72 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'count', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'integer', + ]) + ->addRule('min', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Minimum value to enforce for new documents.', + 'default' => null, + 'required' => false, + 'example' => 1, + ]) + ->addRule('max', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Maximum value to enforce for new documents.', + 'default' => null, + 'required' => false, + 'example' => 10, + ]) + ->addRule('default', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => 10, + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_INTEGER, + ]; + + /** + * Get Name * + * @return string + */ + public function getName(): string + { + return 'AttributeInteger'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_INTEGER; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php new file mode 100644 index 0000000000..8b04475a14 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -0,0 +1,58 @@ +addRule('total', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total number of attributes in the given collection.', + 'default' => 0, + 'example' => 5, + ]) + ->addRule('attributes', [ + 'type' => [ + Response::MODEL_ATTRIBUTE_BOOLEAN, + Response::MODEL_ATTRIBUTE_INTEGER, + Response::MODEL_ATTRIBUTE_FLOAT, + Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_ENUM, + Response::MODEL_ATTRIBUTE_URL, + Response::MODEL_ATTRIBUTE_IP, + Response::MODEL_ATTRIBUTE_DATETIME, + Response::MODEL_ATTRIBUTE_RELATIONSHIP, + Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute + ], + 'description' => 'List of attributes.', + 'default' => [], + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'Attributes List'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_LIST; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php b/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php new file mode 100644 index 0000000000..d88fbd1530 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php @@ -0,0 +1,96 @@ +addRule('relatedCollection', [ + 'type' => self::TYPE_STRING, + 'description' => 'The ID of the related collection.', + 'default' => null, + 'example' => 'collection', + ]) + ->addRule('relationType', [ + 'type' => self::TYPE_STRING, + 'description' => 'The type of the relationship.', + 'default' => '', + 'example' => 'oneToOne|oneToMany|manyToOne|manyToMany', + ]) + ->addRule('twoWay', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is the relationship two-way?', + 'default' => false, + 'example' => false, + ]) + ->addRule('twoWayKey', [ + 'type' => self::TYPE_STRING, + 'description' => 'The key of the two-way relationship.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('onDelete', [ + 'type' => self::TYPE_STRING, + 'description' => 'How deleting the parent document will propagate to child documents.', + 'default' => 'restrict', + 'example' => 'restrict|cascade|setNull', + ]) + ->addRule('side', [ + 'type' => self::TYPE_STRING, + 'description' => 'Whether this is the parent or child side of the relationship', + 'default' => '', + 'example' => 'parent|child', + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_RELATIONSHIP, + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeRelationship'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_RELATIONSHIP; + } + + /** + * Process Document before returning it to the client + * + * @return Document + */ + public function filter(Document $document): Document + { + $options = $document->getAttribute('options'); + if (!\is_null($options)) { + $document->setAttribute('relatedCollection', $options['relatedCollection']); + $document->setAttribute('relationType', $options['relationType']); + $document->setAttribute('twoWay', $options['twoWay']); + $document->setAttribute('twoWayKey', $options['twoWayKey']); + $document->setAttribute('side', $options['side']); + $document->setAttribute('onDelete', $options['onDelete']); + } + return $document; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php new file mode 100644 index 0000000000..12bb42009d --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -0,0 +1,53 @@ +addRule('size', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Attribute size.', + 'default' => 0, + 'example' => 128, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => 'default', + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_STRING, + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeString'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_STRING; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php new file mode 100644 index 0000000000..633d5b49d7 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -0,0 +1,66 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'githubUrl', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => APP_DATABASE_ATTRIBUTE_URL, + 'example' => APP_DATABASE_ATTRIBUTE_URL, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'required' => false, + 'example' => 'http://example.com', + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_URL + ]; + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'AttributeURL'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_ATTRIBUTE_URL; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/BaseList.php b/src/Appwrite/Utopia/Response/Model/BaseList.php index 1fa3197ba7..dea5d476f2 100644 --- a/src/Appwrite/Utopia/Response/Model/BaseList.php +++ b/src/Appwrite/Utopia/Response/Model/BaseList.php @@ -32,9 +32,11 @@ class BaseList extends Model if ($paging) { $namesWithCap = [ - 'rows', 'tables', 'users', 'files', 'buckets', 'functions', - 'deployments', 'executions', 'projects', 'webhooks', 'keys', - 'platforms', 'rules', 'memberships', 'teams' + 'rows', 'tables', // new api + 'documents', 'collections', // legacy api + 'users', 'files', 'buckets', 'functions', + 'deployments', 'executions', 'projects', + 'webhooks', 'keys', 'platforms', 'rules', 'memberships', 'teams' ]; if (\in_array($name, $namesWithCap)) { diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php new file mode 100644 index 0000000000..2c2bf0cca8 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -0,0 +1,109 @@ +addRule('$id', [ + 'type' => self::TYPE_STRING, + 'description' => 'Collection ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('$createdAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Collection creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$updatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Collection update date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$permissions', [ + 'type' => self::TYPE_STRING, + 'description' => 'Collection permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', + 'default' => '', + 'example' => ['read("any")'], + 'array' => true + ]) + ->addRule('databaseId', [ + 'type' => self::TYPE_STRING, + 'description' => 'Database ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('name', [ + 'type' => self::TYPE_STRING, + 'description' => 'Collection name.', + 'default' => '', + 'example' => 'My Collection', + ]) + ->addRule('enabled', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Collection enabled. Can be \'enabled\' or \'disabled\'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys.', + 'default' => true, + 'example' => false, + ]) + ->addRule('documentSecurity', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Whether document-level permissions are enabled. [Learn more about permissions](https://appwrite.io/docs/permissions).', + 'default' => '', + 'example' => true, + ]) + ->addRule('attributes', [ + 'type' => [ + Response::MODEL_ATTRIBUTE_BOOLEAN, + Response::MODEL_ATTRIBUTE_INTEGER, + Response::MODEL_ATTRIBUTE_FLOAT, + Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_ENUM, + Response::MODEL_ATTRIBUTE_URL, + Response::MODEL_ATTRIBUTE_IP, + Response::MODEL_ATTRIBUTE_DATETIME, + Response::MODEL_ATTRIBUTE_RELATIONSHIP, + Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute + ], + 'description' => 'Collection attributes.', + 'default' => [], + 'example' => new \stdClass(), + 'array' => true, + ]) + ->addRule('indexes', [ + 'type' => Response::MODEL_INDEX, + 'description' => 'Collection indexes.', + 'default' => [], + 'example' => new \stdClass(), + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'Collection'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_COLLECTION; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php new file mode 100644 index 0000000000..41a10cee89 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -0,0 +1,92 @@ +addRule('$id', [ + 'type' => self::TYPE_STRING, + 'description' => 'Document ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('$collectionId', [ + 'type' => self::TYPE_STRING, + 'description' => 'Collection ID.', + 'default' => '', + 'example' => '5e5ea5c15117e', + ]) + ->addRule('$databaseId', [ + 'type' => self::TYPE_STRING, + 'description' => 'Database ID.', + 'default' => '', + 'example' => '5e5ea5c15117e', + ]) + ->addRule('$createdAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Document creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$updatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Document update date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$permissions', [ + 'type' => self::TYPE_STRING, + 'description' => 'Document permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', + 'default' => '', + 'example' => ['read("any")'], + 'array' => true, + ]); + } + + public function filter(DatabaseDocument $document): DatabaseDocument + { + $document->removeAttribute('$internalId'); + $document->removeAttribute('$collection'); + $document->removeAttribute('$tenant'); + + foreach ($document->getAttributes() as $attribute) { + if (\is_array($attribute)) { + foreach ($attribute as $subAttribute) { + if ($subAttribute instanceof DatabaseDocument) { + $this->filter($subAttribute); + } + } + } elseif ($attribute instanceof DatabaseDocument) { + $this->filter($attribute); + } + } + + return $document; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/UsageCollection.php b/src/Appwrite/Utopia/Response/Model/UsageCollection.php new file mode 100644 index 0000000000..b2336d65ba --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/UsageCollection.php @@ -0,0 +1,54 @@ +addRule('range', [ + 'type' => self::TYPE_STRING, + 'description' => 'Time range of the usage stats.', + 'default' => '', + 'example' => '30d', + ]) + ->addRule('documentsTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of of documents.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('documents', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of documents per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName(): string + { + return 'UsageCollection'; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return Response::MODEL_USAGE_COLLECTION; + } +} From 7f023f29c56ee7123026d22e060ce2f17d222dfa Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 11:30:31 +0530 Subject: [PATCH 054/173] update: keep both tables and collections api errors. --- app/config/errors.php | 122 ++++++++++++++++++++++++++++-- app/controllers/general.php | 38 ++++++++-- src/Appwrite/Extend/Exception.php | 26 +++++++ 3 files changed, 174 insertions(+), 12 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 979bf43f7d..c1121cb98e 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -71,7 +71,7 @@ return [ ], Exception::GENERAL_QUERY_LIMIT_EXCEEDED => [ 'name' => Exception::GENERAL_QUERY_LIMIT_EXCEEDED, - 'description' => 'Query limit exceeded for the current column. Usage of more than 100 query values on a single column is prohibited.', + 'description' => 'Query limit exceeded for the current attribute/column. Usage of more than 100 query values on a single column is prohibited.', 'code' => 400, ], Exception::GENERAL_QUERY_INVALID => [ @@ -653,7 +653,24 @@ return [ ], Exception::DATABASE_QUERY_ORDER_NULL => [ 'name' => Exception::DATABASE_QUERY_ORDER_NULL, - 'description' => 'The order column had a null value. Cursor pagination requires all rows order column values are non-null.', + 'description' => 'The order attribute/column had a null value. Cursor pagination requires all documents/rows order attribute/column values are non-null.', + 'code' => 400, + ], + + /** Collections */ + Exception::COLLECTION_NOT_FOUND => [ + 'name' => Exception::COLLECTION_NOT_FOUND, + 'description' => 'Collection with the requested ID could not be found.', + 'code' => 404, + ], + Exception::COLLECTION_ALREADY_EXISTS => [ + 'name' => Exception::COLLECTION_ALREADY_EXISTS, + 'description' => 'A collection with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + 'code' => 409, + ], + Exception::COLLECTION_LIMIT_EXCEEDED => [ + 'name' => Exception::COLLECTION_LIMIT_EXCEEDED, + 'description' => 'The maximum number of collections has been reached.', 'code' => 400, ], @@ -674,6 +691,43 @@ return [ 'code' => 400, ], + /** Documents */ + Exception::DOCUMENT_NOT_FOUND => [ + 'name' => Exception::DOCUMENT_NOT_FOUND, + 'description' => 'Document with the requested ID could not be found.', + 'code' => 404, + ], + Exception::DOCUMENT_INVALID_STRUCTURE => [ + 'name' => Exception::DOCUMENT_INVALID_STRUCTURE, + 'description' => 'The document structure is invalid. Please ensure the attributes match the collection definition.', + 'code' => 400, + ], + Exception::DOCUMENT_MISSING_DATA => [ + 'name' => Exception::DOCUMENT_MISSING_DATA, + 'description' => 'The document data is missing. Try again with document data populated', + 'code' => 400, + ], + Exception::DOCUMENT_MISSING_PAYLOAD => [ + 'name' => Exception::DOCUMENT_MISSING_PAYLOAD, + 'description' => 'The document data and permissions are missing. You must provide either document data or permissions to be updated.', + 'code' => 400, + ], + Exception::DOCUMENT_ALREADY_EXISTS => [ + 'name' => Exception::DOCUMENT_ALREADY_EXISTS, + 'description' => 'Document with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + 'code' => 409, + ], + Exception::DOCUMENT_UPDATE_CONFLICT => [ + 'name' => Exception::DOCUMENT_UPDATE_CONFLICT, + 'description' => 'Remote document is newer than local.', + 'code' => 409, + ], + Exception::DOCUMENT_DELETE_RESTRICTED => [ + 'name' => Exception::DOCUMENT_DELETE_RESTRICTED, + 'description' => 'Document cannot be deleted because it is referenced by another document.', + 'code' => 403, + ], + /** Rows */ Exception::ROW_NOT_FOUND => [ 'name' => Exception::ROW_NOT_FOUND, @@ -711,6 +765,65 @@ return [ 'code' => 403, ], + /** Attributes */ + Exception::ATTRIBUTE_NOT_FOUND => [ + 'name' => Exception::ATTRIBUTE_NOT_FOUND, + 'description' => 'Attribute with the requested ID could not be found.', + 'code' => 404, + ], + Exception::ATTRIBUTE_UNKNOWN => [ + 'name' => Exception::ATTRIBUTE_UNKNOWN, + 'description' => 'The attribute required for the index could not be found. Please confirm all your attributes are in the available state.', + 'code' => 400, + ], + Exception::ATTRIBUTE_NOT_AVAILABLE => [ + 'name' => Exception::ATTRIBUTE_NOT_AVAILABLE, + 'description' => 'The requested attribute is not yet available. Please try again later.', + 'code' => 400, + ], + Exception::ATTRIBUTE_FORMAT_UNSUPPORTED => [ + 'name' => Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, + 'description' => 'The requested attribute format is not supported.', + 'code' => 400, + ], + Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED => [ + 'name' => Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, + 'description' => 'Default values cannot be set for array or required attributes.', + 'code' => 400, + ], + Exception::ATTRIBUTE_ALREADY_EXISTS => [ + 'name' => Exception::ATTRIBUTE_ALREADY_EXISTS, + 'description' => 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', + 'code' => 409, + ], + Exception::ATTRIBUTE_LIMIT_EXCEEDED => [ + 'name' => Exception::ATTRIBUTE_LIMIT_EXCEEDED, + 'description' => 'The maximum number or size of attributes for this collection has been reached.', + 'code' => 400, + ], + Exception::ATTRIBUTE_VALUE_INVALID => [ + 'name' => Exception::ATTRIBUTE_VALUE_INVALID, + 'description' => 'The attribute value is invalid. Please check the type, range and value of the attribute.', + 'code' => 400, + ], + Exception::ATTRIBUTE_TYPE_INVALID => [ + 'name' => Exception::ATTRIBUTE_TYPE_INVALID, + 'description' => 'The attribute type is invalid.', + 'code' => 400, + ], + Exception::ATTRIBUTE_INVALID_RESIZE => [ + 'name' => Exception::ATTRIBUTE_INVALID_RESIZE, + 'description' => "Existing data is too large for new size, truncate your existing data then try again.", + 'code' => 400, + ], + + /** Exists for both Attributes & Columns */ + Exception::RELATIONSHIP_VALUE_INVALID => [ + 'name' => Exception::RELATIONSHIP_VALUE_INVALID, + 'description' => 'The relationship value is invalid.', + 'code' => 400, + ], + /** Columns */ Exception::COLUMN_NOT_FOUND => [ 'name' => Exception::COLUMN_NOT_FOUND, @@ -757,11 +870,6 @@ return [ 'description' => 'The column type is invalid.', 'code' => 400, ], - Exception::RELATIONSHIP_VALUE_INVALID => [ - 'name' => Exception::RELATIONSHIP_VALUE_INVALID, - 'description' => 'The relationship value is invalid.', - 'code' => 400, - ], Exception::COLUMN_INVALID_RESIZE => [ 'name' => Exception::COLUMN_INVALID_RESIZE, 'description' => "Existing data is too large for new size, truncate your existing data then try again.", diff --git a/app/controllers/general.php b/app/controllers/general.php index cc6739e790..da68d4758c 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1148,6 +1148,9 @@ App::error() Console::error('[Error] Line: ' . $line); } + // routes like /tables, /tables/:tableId, etc. + $isTablesAPI = str_contains($route->getPath(), '/databases/:databaseId/tables'); + switch ($class) { case 'Utopia\Exception': $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); @@ -1161,7 +1164,12 @@ App::error() } break; case 'Utopia\Database\Exception\Conflict': - $error = new AppwriteException(AppwriteException::ROW_UPDATE_CONFLICT, previous: $error); + $error = new AppwriteException( + $isTablesAPI + ? AppwriteException::ROW_UPDATE_CONFLICT + : AppwriteException::DOCUMENT_UPDATE_CONFLICT, + previous: $error + ); break; case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); @@ -1170,13 +1178,27 @@ App::error() $error = new AppwriteException(AppwriteException::GENERAL_QUERY_INVALID, $error->getMessage(), previous: $error); break; case 'Utopia\Database\Exception\Structure': - $error = new AppwriteException(AppwriteException::ROW_INVALID_STRUCTURE, $error->getMessage(), previous: $error); + $error = new AppwriteException( + $isTablesAPI + ? AppwriteException::ROW_INVALID_STRUCTURE + : AppwriteException::DOCUMENT_INVALID_STRUCTURE, + $error->getMessage(), + previous: $error + ); break; case 'Utopia\Database\Exception\Duplicate': - $error = new AppwriteException(AppwriteException::ROW_ALREADY_EXISTS); + $error = new AppwriteException( + $isTablesAPI + ? AppwriteException::ROW_ALREADY_EXISTS + : AppwriteException::DOCUMENT_ALREADY_EXISTS + ); break; case 'Utopia\Database\Exception\Restricted': - $error = new AppwriteException(AppwriteException::ROW_DELETE_RESTRICTED); + $error = new AppwriteException( + $isTablesAPI + ? AppwriteException::ROW_DELETE_RESTRICTED + : AppwriteException::DOCUMENT_DELETE_RESTRICTED + ); break; case 'Utopia\Database\Exception\Authorization': $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); @@ -1185,7 +1207,13 @@ App::error() $error = new AppwriteException(AppwriteException::RELATIONSHIP_VALUE_INVALID, $error->getMessage(), previous: $error); break; case 'Utopia\Database\Exception\NotFound': - $error = new AppwriteException(AppwriteException::TABLE_NOT_FOUND, $error->getMessage(), previous: $error); + $error = new AppwriteException( + $isTablesAPI + ? AppwriteException::TABLE_NOT_FOUND + : AppwriteException::COLLECTION_NOT_FOUND, + $error->getMessage(), + previous: $error + ); break; case 'Utopia\Database\Exception\Dependency': $error = new AppwriteException(AppwriteException::INDEX_DEPENDENCY, null, previous: $error); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 31a52a6a04..15b964577d 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -192,10 +192,24 @@ class Exception extends \Exception public const DATABASE_QUERY_ORDER_NULL = 'database_query_order_null'; /** Collections */ + public const COLLECTION_NOT_FOUND = 'collection_not_found'; + public const COLLECTION_ALREADY_EXISTS = 'collection_already_exists'; + public const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded'; + + /** Tables */ public const TABLE_NOT_FOUND = 'table_not_found'; public const TABLE_ALREADY_EXISTS = 'table_already_exists'; public const TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; + /** Documents */ + public const DOCUMENT_NOT_FOUND = 'document_not_found'; + public const DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure'; + public const DOCUMENT_MISSING_DATA = 'document_missing_data'; + public const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload'; + public const DOCUMENT_ALREADY_EXISTS = 'document_already_exists'; + public const DOCUMENT_UPDATE_CONFLICT = 'document_update_conflict'; + public const DOCUMENT_DELETE_RESTRICTED = 'document_delete_restricted'; + /** Rows */ public const ROW_NOT_FOUND = 'row_not_found'; public const ROW_INVALID_STRUCTURE = 'row_invalid_structure'; @@ -205,6 +219,18 @@ class Exception extends \Exception public const ROW_UPDATE_CONFLICT = 'row_update_conflict'; public const ROW_DELETE_RESTRICTED = 'row_delete_restricted'; + /** Attributes */ + public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found'; + public const ATTRIBUTE_UNKNOWN = 'attribute_unknown'; + public const ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available'; + public const ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported'; + public const ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported'; + public const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists'; + public const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded'; + public const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid'; + public const ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid'; + public const ATTRIBUTE_INVALID_RESIZE = 'attribute_invalid_resize'; + /** Columns */ public const COLUMN_NOT_FOUND = 'column_not_found'; public const COLUMN_UNKNOWN = 'column_unknown'; From 8610687b873df10bfb072b2f4e7f4d8674fabdf3 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 13:23:27 +0530 Subject: [PATCH 055/173] update: events schema, response models for database usages. --- app/config/events.php | 2 +- .../Databases/Http/Databases/Usage/Get.php | 8 +- .../Databases/Http/Databases/Usage/XList.php | 8 +- src/Appwrite/Utopia/Response.php | 5 + .../Utopia/Response/Model/ColumnIndex.php | 94 +++++++++++++++++++ src/Appwrite/Utopia/Response/Model/Index.php | 15 +-- src/Appwrite/Utopia/Response/Model/Table.php | 2 +- .../Utopia/Response/Model/UsageDatabase.php | 17 ++-- .../Utopia/Response/Model/UsageDatabases.php | 17 ++-- .../Utopia/Response/Model/UsageProject.php | 5 +- 10 files changed, 131 insertions(+), 42 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/ColumnIndex.php diff --git a/app/config/events.php b/app/config/events.php index 182aa91363..e7b079050c 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -114,7 +114,7 @@ return [ ], ], 'indexes' => [ - '$model' => Response::MODEL_INDEX, + '$model' => Response::MODEL_COLUMN_INDEX, '$resource' => true, '$description' => 'This event triggers on any indexes event.', 'create' => [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index c7ae2e4d81..c4105effa0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -123,13 +123,13 @@ class Get extends Action $response->dynamic(new Document([ 'range' => $range, - 'tablesTotal' => $usage[$metrics[0]]['total'], - 'rowsTotal' => $usage[$metrics[1]]['total'], + 'collectionsTotal' => $usage[$metrics[0]]['total'], + 'documentsTotal' => $usage[$metrics[1]]['total'], 'storageTotal' => $usage[$metrics[2]]['total'], 'databaseReadsTotal' => $usage[$metrics[3]]['total'], 'databaseWritesTotal' => $usage[$metrics[4]]['total'], - 'tables' => $usage[$metrics[0]]['data'], - 'rows' => $usage[$metrics[1]]['data'], + 'collections' => $usage[$metrics[0]]['data'], + 'documents' => $usage[$metrics[1]]['data'], 'storage' => $usage[$metrics[2]]['data'], 'databaseReads' => $usage[$metrics[3]]['data'], 'databaseWrites' => $usage[$metrics[4]]['data'], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index 8737585e8c..0078d54a4b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -116,14 +116,14 @@ class XList extends Action $response->dynamic(new Document([ 'range' => $range, 'databasesTotal' => $usage[$metrics[0]]['total'], - 'tablesTotal' => $usage[$metrics[1]]['total'], - 'rowsTotal' => $usage[$metrics[2]]['total'], + 'collectionsTotal' => $usage[$metrics[1]]['total'], + 'documentsTotal' => $usage[$metrics[2]]['total'], 'storageTotal' => $usage[$metrics[3]]['total'], 'databasesReadsTotal' => $usage[$metrics[4]]['total'], 'databasesWritesTotal' => $usage[$metrics[5]]['total'], 'databases' => $usage[$metrics[0]]['data'], - 'tables' => $usage[$metrics[1]]['data'], - 'rows' => $usage[$metrics[2]]['data'], + 'collections' => $usage[$metrics[1]]['data'], + 'documents' => $usage[$metrics[2]]['data'], 'storage' => $usage[$metrics[3]]['data'], 'databasesReads' => $usage[$metrics[4]]['data'], 'databasesWrites' => $usage[$metrics[5]]['data'], diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index c72a01c79a..cec275869a 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -38,6 +38,7 @@ use Appwrite\Utopia\Response\Model\ColumnDatetime; use Appwrite\Utopia\Response\Model\ColumnEmail; use Appwrite\Utopia\Response\Model\ColumnEnum; use Appwrite\Utopia\Response\Model\ColumnFloat; +use Appwrite\Utopia\Response\Model\ColumnIndex; use Appwrite\Utopia\Response\Model\ColumnInteger; use Appwrite\Utopia\Response\Model\ColumnIP; use Appwrite\Utopia\Response\Model\ColumnList; @@ -182,6 +183,8 @@ class Response extends SwooleResponse public const MODEL_TABLE_LIST = 'tableList'; public const MODEL_INDEX = 'index'; public const MODEL_INDEX_LIST = 'indexList'; + public const MODEL_COLUMN_INDEX = 'columnIndex'; + public const MODEL_COLUMN_INDEX_LIST = 'columnIndexList'; public const MODEL_DOCUMENT = 'document'; public const MODEL_DOCUMENT_LIST = 'documentList'; public const MODEL_ROW = 'row'; @@ -416,6 +419,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) + ->setModel(new BaseList('Column Indexes List', self::MODEL_COLUMN_INDEX_LIST, 'indexes', self::MODEL_COLUMN_INDEX)) ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) ->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY)) @@ -493,6 +497,7 @@ class Response extends SwooleResponse ->setModel(new ColumnDatetime()) ->setModel(new ColumnRelationship()) ->setModel(new Index()) + ->setModel(new ColumnIndex()) ->setModel(new Row()) ->setModel(new ModelDocument()) ->setModel(new Log()) diff --git a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php new file mode 100644 index 0000000000..8c632266ef --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php @@ -0,0 +1,94 @@ +addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Index Key.', + 'default' => '', + 'example' => 'index1', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Index type.', + 'default' => '', + 'example' => 'primary', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', + 'default' => '', + 'example' => 'available', + ]) + ->addRule('error', [ + 'type' => self::TYPE_STRING, + 'description' => 'Error message. Displays error generated on failure of creating or deleting an index.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('columns', [ + 'type' => self::TYPE_STRING, + 'description' => 'Index columns.', + 'default' => [], + 'example' => [], + 'array' => true, + ]) + ->addRule('orders', [ + 'type' => self::TYPE_STRING, + 'description' => 'Index orders.', + 'default' => [], + 'example' => [], + 'array' => true, + 'required' => false, + ]) + ->addRule('$createdAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Index creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$updatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Index update date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]); + } + + /** + * Get Name + */ + public function getName(): string + { + return 'Index'; + } + + /** + * Get Collection + */ + public function getType(): string + { + return Response::MODEL_COLUMN_INDEX; + } + + public function filter(Document $document): Document + { + + $columns = $document->getAttribute('attributes', []); + $document + ->removeAttribute('attributes') + ->setAttribute('columns', $columns); + + return $document; + + } +} diff --git a/src/Appwrite/Utopia/Response/Model/Index.php b/src/Appwrite/Utopia/Response/Model/Index.php index e18a9db334..2d795ad439 100644 --- a/src/Appwrite/Utopia/Response/Model/Index.php +++ b/src/Appwrite/Utopia/Response/Model/Index.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use Utopia\Database\Document; class Index extends Model { @@ -35,7 +34,7 @@ class Index extends Model 'default' => '', 'example' => 'string', ]) - ->addRule('columns', [ + ->addRule('attributes', [ 'type' => self::TYPE_STRING, 'description' => 'Index attributes.', 'default' => [], @@ -79,16 +78,4 @@ class Index extends Model { return Response::MODEL_INDEX; } - - public function filter(Document $document): Document - { - - $columns = $document->getAttribute('attributes', []); - $document - ->removeAttribute('attributes') - ->setAttribute('columns', $columns); - - return $document; - - } } diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index 1855f471b1..1b890d9e60 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -79,7 +79,7 @@ class Table extends Model 'array' => true, ]) ->addRule('indexes', [ - 'type' => Response::MODEL_INDEX, + 'type' => Response::MODEL_COLUMN_INDEX, 'description' => 'Table indexes.', 'default' => [], 'example' => new \stdClass(), diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index a3212017a4..3adabae4c1 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -5,6 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +// TODO: check what do we use for - collectionsTotal, documentsTotal, collections, documents class UsageDatabase extends Model { public function __construct() @@ -16,15 +17,15 @@ class UsageDatabase extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('tablesTotal', [ + ->addRule('collectionsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of tables.', + 'description' => 'Total aggregated number of collections.', 'default' => 0, 'example' => 0, ]) - ->addRule('rowsTotal', [ + ->addRule('collectionsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of rows.', + 'description' => 'Total aggregated number of documents.', 'default' => 0, 'example' => 0, ]) @@ -46,16 +47,16 @@ class UsageDatabase extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('tables', [ + ->addRule('collections', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of tables per period.', + 'description' => 'Aggregated number of collections per period.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('rows', [ + ->addRule('documents', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of rows per period.', + 'description' => 'Aggregated number of documents per period.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php index 11392f6efb..ae93182c0f 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php @@ -5,6 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +// TODO: check what do we use for - collectionsTotal, documentsTotal, collections, documents class UsageDatabases extends Model { public function __construct() @@ -22,15 +23,15 @@ class UsageDatabases extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('tablesTotal', [ + ->addRule('collectionsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of tables.', + 'description' => 'Total aggregated number of collections.', 'default' => 0, 'example' => 0, ]) - ->addRule('rowsTotal', [ + ->addRule('documentsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of rows.', + 'description' => 'Total aggregated number of documents.', 'default' => 0, 'example' => 0, ]) @@ -59,16 +60,16 @@ class UsageDatabases extends Model 'example' => [], 'array' => true ]) - ->addRule('tables', [ + ->addRule('collections', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of tables per period.', + 'description' => 'Aggregated number of collections per period.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('rows', [ + ->addRule('documents', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated number of rows per period.', + 'description' => 'Aggregated number of documents per period.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index b79cd85bed..70f8003cfd 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -5,6 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +// TODO: check what do we use for - documents. class UsageProject extends Model { public function __construct() @@ -16,9 +17,9 @@ class UsageProject extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('rowsTotal', [ + ->addRule('documentsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of rows.', + 'description' => 'Total aggregated number of documents.', 'default' => 0, 'example' => 0, ]) From f2f23be648f9c3c8c1e07ee073a4c35f3f1f7f5c Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 14:11:28 +0530 Subject: [PATCH 056/173] update: events, errors, realtime. --- app/config/errors.php | 2 +- app/config/events.php | 2 +- app/config/templates/function.php | 5 +- src/Appwrite/Event/Database.php | 56 ++++++++++++- src/Appwrite/Messaging/Adapter/Realtime.php | 7 +- .../Modules/Databases/Workers/Databases.php | 78 ++++++++++++------- 6 files changed, 114 insertions(+), 36 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index c1121cb98e..182b6286e8 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -71,7 +71,7 @@ return [ ], Exception::GENERAL_QUERY_LIMIT_EXCEEDED => [ 'name' => Exception::GENERAL_QUERY_LIMIT_EXCEEDED, - 'description' => 'Query limit exceeded for the current attribute/column. Usage of more than 100 query values on a single column is prohibited.', + 'description' => 'Query limit exceeded for the current attribute/column. Usage of more than 100 query values on a single attribute/column is prohibited.', 'code' => 400, ], Exception::GENERAL_QUERY_INVALID => [ diff --git a/app/config/events.php b/app/config/events.php index e7b079050c..b0e6b9f5fc 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -152,7 +152,7 @@ return [ 'documents' => [ '$model' => Response::MODEL_DOCUMENT, '$resource' => true, - '$description' => 'This event triggers on any document event.', + '$description' => 'This event triggers on any documents event.', 'create' => [ '$description' => 'This event triggers when a document is created.', ], diff --git a/app/config/templates/function.php b/app/config/templates/function.php index b017281dbe..960bc1d65e 100644 --- a/app/config/templates/function.php +++ b/app/config/templates/function.php @@ -1395,7 +1395,10 @@ return [ 'score' => 5, 'tagline' => 'Convert text to speech using the Hugging Face inference API.', 'permissions' => ['any'], - 'events' => ['databases.*.tables.*.rows.*.create'], + 'events' => [ + 'databases.*.tables.*.rows.*.create', + 'databases.*.collections.*.documents.*.create', + ], 'cron' => '', 'timeout' => 15, 'useCases' => ['ai'], diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 797574d2fc..be73614dc1 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -10,8 +10,15 @@ class Database extends Event { protected string $type = ''; protected ?Document $database = null; - protected ?Document $table = null; + + // tables api protected ?Document $row = null; + protected ?Document $table = null; + + // collections api + protected ?Document $document = null; + protected ?Document $collection = null; + public function __construct(protected Publisher $publisher) { @@ -99,6 +106,51 @@ class Database extends Event return $this->row; } + /** + * Set the collection for this database event. + * + * @param Document $collection + * @return self + */ + public function setCollection(Document $collection): self + { + $this->collection = $collection; + + return $this; + } + + /** + * Returns set collection for this event. + * + * @return null|Document + */ + public function getCollection(): ?Document + { + return $this->collection; + } + + /** + * Set the document for this database event. + * + * @param Document $document + * @return self + */ + public function setDocument(Document $document): self + { + $this->document = $document; + + return $this; + } + + /** + * Returns set document for this database event. + * @return null|Document + */ + public function getDocument(): ?Document + { + return $this->document; + } + public function getQueue(): string { try { @@ -125,6 +177,8 @@ class Database extends Event 'type' => $this->type, 'table' => $this->table, 'row' => $this->row, + 'collection' => $this->collection, + 'document' => $this->document, 'database' => $this->database, 'events' => Event::generateEvents($this->getEvent(), $this->getParams()) ]; diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 5cb908464f..4980618ab1 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -230,7 +230,7 @@ class Realtime extends Adapter foreach ($channels as $key => $value) { switch (true) { - case strpos($key, 'account.') === 0: + case str_starts_with($key, 'account.'): unset($channels[$key]); break; @@ -298,12 +298,13 @@ class Realtime extends Adapter $roles = [Role::team(ID::custom($parts[1]))->toString()]; break; case 'databases': - if (in_array($parts[4] ?? [], ['columns', 'indexes'])) { + $resource = $parts[4] ?? ''; + if (in_array($resource, ['columns', 'attributes', 'indexes'])) { $channels[] = 'console'; $channels[] = 'projects.' . $project->getId(); $projectId = 'console'; $roles = [Role::team($project->getAttribute('teamId'))->toString()]; - } elseif (($parts[4] ?? '') === 'rows' || ($parts[4] ?? '') === 'documents') { + } elseif (in_array($resource, ['rows', 'documents'])) { if ($database->isEmpty()) { throw new \Exception('Database needs to be passed to Realtime for Document/Row events in the Database.'); } diff --git a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 37fa7cf0cc..2a6d1f1f28 100644 --- a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -60,8 +60,8 @@ class Databases extends Action } $type = $payload['type']; - $row = new Document($payload['row'] ?? []); - $table = new Document($payload['table'] ?? []); + $row = new Document($payload['row'] ?? $payload['document'] ?? []); + $table = new Document($payload['table'] ?? $payload['collection'] ?? []); $database = new Document($payload['database'] ?? []); $log->addTag('projectId', $project->getId()); @@ -115,10 +115,13 @@ class Databases extends Action } $projectId = $project->getId(); - $event = "databases.[databaseId].tables.[tableId].columns.[columnId].update"; + $events = [ + "databases.[databaseId].tables.[tableId].columns.[columnId].update", + "databases.[databaseId].collections.[collectionId].attributes.[attributeId].update", + ]; /** * TODO @christyjacob4 verify if this is still the case - * Fetch attribute from the database, since with Resque float values are loosing informations. + * Fetch attribute from the database, since with Resque float values are loosing information. */ $column = $dbForProject->getDocument('attributes', $column->getId()); @@ -204,7 +207,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $table, $project, $event, $queueForRealtime, $column); + $this->trigger($database, $table, $project, $events, $queueForRealtime, $column); if (! $relatedTable->isEmpty()) { $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); @@ -238,7 +241,10 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].tables.[tableId].columns.[columnId].delete'; + $events = [ + 'databases.[databaseId].tables.[tableId].columns.[columnId].delete', + 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', + ]; $tableId = $table->getId(); $key = $column->getAttribute('key', ''); $type = $column->getAttribute('type', ''); @@ -311,7 +317,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $table, $project, $event, $queueForRealtime, $column); + $this->trigger($database, $table, $project, $events, $queueForRealtime, $column); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -398,7 +404,10 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].tables.[tableId].indexes.[indexId].update'; + $events = [ + 'databases.[databaseId].tables.[tableId].indexes.[indexId].update', + 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update', + ]; $collectionId = $table->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); @@ -425,7 +434,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $table, $project, $event, $queueForRealtime, null, $index); + $this->trigger($database, $table, $project, $events, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } @@ -455,7 +464,10 @@ class Databases extends Action } $projectId = $project->getId(); - $event = 'databases.[databaseId].tables.[tableId].indexes.[indexId].delete'; + $events = [ + 'databases.[databaseId].tables.[tableId].indexes.[indexId].delete', + 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', + ]; $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); $project = $dbForPlatform->getDocument('projects', $projectId); @@ -481,7 +493,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $table, $project, $event, $queueForRealtime, null, $index); + $this->trigger($database, $table, $project, $events, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $table->getId()); } } @@ -590,38 +602,46 @@ class Databases extends Action * @param Document $database * @param Document $table * @param Document $project + * @param string[] $events * @param Realtime $queueForRealtime * @param Document|null $column * @param Document|null $index * @return void + * @throws DatabaseException */ protected function trigger( Document $database, Document $table, Document $project, - string $event, + array $events, Realtime $queueForRealtime, Document|null $column = null, Document|null $index = null, ): void { - $queueForRealtime - ->setProject($project) - ->setSubscribers(['console']) - ->setEvent($event) - ->setParam('databaseId', $database->getId()) - ->setParam('tableId', $table->getId()); - - if (! empty($column)) { + // table and collection + foreach ($events as $event) { $queueForRealtime - ->setParam('columnId', $column->getId()) - ->setPayload($column->getArrayCopy()); - } - if (! empty($index)) { - $queueForRealtime - ->setParam('indexId', $index->getId()) - ->setPayload($index->getArrayCopy()); - } + ->setProject($project) + ->setSubscribers(['console']) + ->setEvent($event) + ->setParam('tableId', $table->getId()) + ->setParam('collectionId', $table->getId()) + ->setParam('databaseId', $database->getId()); - $queueForRealtime->trigger(); + if (! empty($column)) { + $queueForRealtime + ->setParam('columnId', $column->getId()) + ->setParam('attributeId', $column->getId()) + ->setPayload($column->getArrayCopy()); + } + + if (! empty($index)) { + $queueForRealtime + ->setParam('indexId', $index->getId()) + ->setPayload($index->getArrayCopy()); + } + + $queueForRealtime->trigger(); + } } } From 6d1afea436d9e2cecbee3aca27dd409615ce9889 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 14:14:12 +0530 Subject: [PATCH 057/173] misc. --- src/Appwrite/Utopia/Response/Model/Webhook.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Webhook.php b/src/Appwrite/Utopia/Response/Model/Webhook.php index 8e53b41e6e..af1e23447e 100644 --- a/src/Appwrite/Utopia/Response/Model/Webhook.php +++ b/src/Appwrite/Utopia/Response/Model/Webhook.php @@ -49,7 +49,10 @@ class Webhook extends Model 'type' => self::TYPE_STRING, 'description' => 'Webhook trigger events.', 'default' => [], - 'example' => 'database.tables.update', + 'example' => [ + 'databases.tables.update', + 'databases.collections.update' + ], 'array' => true, ]) ->addRule('security', [ From 0f94b800a52201e18426bc97f683db3a8259dd2b Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 17:02:24 +0530 Subject: [PATCH 058/173] update: abstraction over table and collection apis. --- .../Databases/Http/Collections/Action.php | 124 ++++++++++++++ .../Databases/Http/Collections/Create.php | 123 +++++++++++++ .../Databases/Http/Collections/Delete.php | 109 ++++++++++++ .../Databases/Http/Collections/Get.php | 81 +++++++++ .../Databases/Http/Collections/Logs/XList.php | 161 ++++++++++++++++++ .../Databases/Http/Collections/Update.php | 117 +++++++++++++ .../Databases/Http/Collections/Usage/Get.php | 143 ++++++++++++++++ .../Databases/Http/Collections/XList.php | 128 ++++++++++++++ .../Modules/Databases/Http/Tables/Create.php | 75 ++------ .../Modules/Databases/Http/Tables/Delete.php | 55 ++---- .../Modules/Databases/Http/Tables/Get.php | 42 ++--- .../Databases/Http/Tables/Logs/XList.php | 113 ++---------- .../Modules/Databases/Http/Tables/Update.php | 64 ++----- .../Databases/Http/Tables/Usage/Get.php | 95 ++--------- .../Modules/Databases/Http/Tables/XList.php | 82 ++------- .../Modules/Databases/Services/Http.php | 21 ++- 16 files changed, 1116 insertions(+), 417 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php new file mode 100644 index 0000000000..d2129f31ea --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php @@ -0,0 +1,124 @@ +context = $context; + } + + /** + * Get the current API context. + * + * @throws \Exception if context has not been set. + */ + final protected function getContext(): string + { + if ($this->context === null) { + throw new \Exception('Missing context: you must call setContext() with either `Action::TABLE` or `Action::COLLECTION` before using this method.'); + } + + return $this->context; + } + + /** + * Get the key used in event parameters (e.g., 'collectionId' or 'tableId'). + */ + final protected function getEventsParamKey(): string + { + return $this->getContext() . 'Id'; + } + + /** + * Get the response model used in the SDK and HTTP responses. + */ + abstract protected function getResponseModel(): string; + + /** + * Determine if the current action is for the Collections API. + */ + final protected function isCollectionsAPI(): bool + { + return $this->getContext() === self::COLLECTION; + } + + /** + * Get the SDK group name for the current action. + */ + final protected function getSdkGroup(): string + { + return $this->isCollectionsAPI() ? 'collections' : 'tables'; + } + + /** + * Get the exception to throw when the resource already exists. + */ + final protected function getDuplicateException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_ALREADY_EXISTS + : Exception::TABLE_ALREADY_EXISTS; + } + + /** + * Get the exception to throw when the resource is not found. + */ + final protected function getNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_NOT_FOUND + : Exception::TABLE_NOT_FOUND; + } + + /** + * Get the exception to throw when the resource limit is exceeded. + */ + final protected function getLimitException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_LIMIT_EXCEEDED + : Exception::TABLE_LIMIT_EXCEEDED; + } + + /** + * Ensures that a valid context has been set. + * + * @throws \Exception if context is missing + */ + final protected function validateContext(): void + { + $this->getContext(); // Triggers exception if not set + } + +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php new file mode 100644 index 0000000000..d0320c6dd0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php @@ -0,0 +1,123 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections') + ->desc('Create collections') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].create') + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'collection.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $this->validateContext(); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collectionId = $collectionId === 'unique()' ? ID::unique() : $collectionId; + + $permissions = Permission::aggregate($permissions) ?? []; + + try { + $collection = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ + '$id' => $collectionId, + 'databaseInternalId' => $database->getInternalId(), + 'databaseId' => $databaseId, + '$permissions' => $permissions, + 'documentSecurity' => $documentSecurity, + 'enabled' => $enabled, + 'name' => $name, + 'search' => \implode(' ', [$collectionId, $name]), + ])); + + $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (LimitException) { + throw new Exception($this->getLimitException()); + } + + $queueForEvents + ->setContext('database', $database) + ->setParam('databaseId', $databaseId) + ->setParam($this->getEventsParamKey(), $collection->getId()); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($collection, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php new file mode 100644 index 0000000000..d30c37efed --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php @@ -0,0 +1,109 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId') + ->desc('Delete collection') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].delete') + ->label('audits.event', 'collection.delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/delete-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { + $this->validateContext(); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + if ($collection->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $collectionId)) { + $type = $this->getContext(); + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Failed to remove $type from DB"); + } + + $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_COLLECTION) + ->setDatabase($database); + + if ($this->isCollectionsAPI()) { + $queueForDatabase->setCollection($collection); + } else { + $queueForDatabase->setTable($collection); + } + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam($this->getEventsParamKey(), $collection->getId()) + ->setPayload($response->output($collection, $this->getResponseModel())); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php new file mode 100644 index 0000000000..962ad3d21d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php @@ -0,0 +1,81 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId') + ->desc('Get collection') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/get-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject): void + { + $this->validateContext(); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + + if ($collection->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + $response->dynamic($collection, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php new file mode 100644 index 0000000000..429c39dc44 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php @@ -0,0 +1,161 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/logs') + ->desc('List collection logs') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/get-collection-logs.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) + ->inject('response') + ->inject('dbForProject') + ->inject('locale') + ->inject('geodb') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + { + $this->validateContext(); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + + if ($collection->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + // Temp fix for logs + $queries[] = Query::or([ + Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), + Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), + ]); + + $audit = new Audit($dbForProject); + $resource = 'database/' . $databaseId . '/' . $this->getContext() . '/' . $collectionId; + $logs = $audit->getLogsByResource($resource, $queries); + + $output = []; + + foreach ($logs as $i => &$log) { + $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; + + $detector = new Detector($log['userAgent']); + $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) + + $os = $detector->getOS(); + $client = $detector->getClient(); + $device = $detector->getDevice(); + + $output[$i] = new Document([ + 'event' => $log['event'], + 'userId' => $log['data']['userId'], + 'userEmail' => $log['data']['userEmail'] ?? null, + 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'ip' => $log['ip'], + 'time' => $log['time'], + 'osCode' => $os['osCode'], + 'osName' => $os['osName'], + 'osVersion' => $os['osVersion'], + 'clientType' => $client['clientType'], + 'clientCode' => $client['clientCode'], + 'clientName' => $client['clientName'], + 'clientVersion' => $client['clientVersion'], + 'clientEngine' => $client['clientEngine'], + 'clientEngineVersion' => $client['clientEngineVersion'], + 'deviceName' => $device['deviceName'], + 'deviceBrand' => $device['deviceBrand'], + 'deviceModel' => $device['deviceModel'] + ]); + + $record = $geodb->get($log['ip']); + + if ($record) { + $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; + $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); + } else { + $output[$i]['countryCode'] = '--'; + $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); + } + } + + $response->dynamic(new Document([ + 'logs' => $output, + 'total' => $audit->countLogsByResource($resource, $queries), + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php new file mode 100644 index 0000000000..582f0e84a3 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php @@ -0,0 +1,117 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId') + ->desc('Update collection') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].update') + ->label('audits.event', 'collection.update') + ->label('audits.resource', 'database/{request.databaseId}/collections/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-collection.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { + $this->validateContext(); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + if ($collection->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + $permissions ??= $collection->getPermissions() ?? []; + + // Map aggregate permissions into the multiple permissions they represent. + $permissions = Permission::aggregate($permissions); + + $enabled ??= $collection->getAttribute('enabled', true); + + $collection = $dbForProject->updateDocument( + 'database_' . $database->getInternalId(), + $collectionId, + $collection + ->setAttribute('name', $name) + ->setAttribute('$permissions', $permissions) + ->setAttribute('documentSecurity', $documentSecurity) + ->setAttribute('enabled', $enabled) + ->setAttribute('search', \implode(' ', [$collectionId, $name])) + ); + + $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity); + + $queueForEvents + ->setContext('database', $database) + ->setParam('databaseId', $databaseId) + ->setParam($this->getEventsParamKey(), $collection->getId()); + + $response->dynamic($collection, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php new file mode 100644 index 0000000000..a9a924114f --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php @@ -0,0 +1,143 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/usage') + ->desc('Get collection usage stats') + ->groups(['api', 'database', 'usage']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: null, + name: self::getName(), + description: '/docs/references/databases/get-collection-usage.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel() + ) + ], + contentType: ContentType::JSON, + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) + ->param('collectionId', '', new UID(), 'Collection ID.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject): void + { + $this->validateContext(); + + $database = $dbForProject->getDocument('databases', $databaseId); + $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + + if ($collection->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), + ]; + + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $result = $dbForProject->findOne('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', ['inf']) + ]); + + $stats[$metric]['total'] = $result['value'] ?? 0; + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', [$period]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric]['data'] = []; + foreach ($results as $result) { + $stats[$metric]['data'][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; + } + } + }); + + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric]['total'] = $stats[$metric]['total']; + $usage[$metric]['data'] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric]['data'][] = [ + 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; + } + } + + $prefix = $this->isCollectionsAPI() ? 'documents' : 'rows'; + + // prefix, prefixTotal + $usageDocument = new Document([ + 'range' => $range, + $prefix => $usage[$metrics[0]]['data'], + $prefix . 'Total' => $usage[$metrics[0]]['total'], + ]); + + $response->dynamic($usageDocument, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php new file mode 100644 index 0000000000..e540e86c06 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php @@ -0,0 +1,128 @@ +setContext(Action::COLLECTION); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections') + ->desc('List collections') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/list-collections.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('queries', [], new Collections(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Collections::ALLOWED_ATTRIBUTES), true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void + { + $this->validateContext(); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $queries = Query::parseQueries($queries); + + if (!empty($search)) { + $queries[] = Query::search('search', $search); + } + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + $cursor = reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $collectionIdId = $cursor->getValue(); + $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionIdId); + + if ($cursorDocument->isEmpty()) { + $message = + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, ucfirst($this->getContext()) . " '$collectionIdId' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + + $filterQueries = Query::groupByType($queries)['filters']; + + try { + $collections = $dbForProject->find('database_' . $database->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); + } catch (OrderException $e) { + $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; + $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; + $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } + + $response->dynamic(new Document([ + 'total' => $total, + $this->getSdkGroup() => $collections, + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php index dc3f7b8574..6f30c897b4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php @@ -3,7 +3,8 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\Create as CollectionCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -11,21 +12,14 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Exception\Limit as LimitException; -use Utopia\Database\Helpers\ID; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; -class Create extends Action +class Create extends CollectionCreate { use HTTP; @@ -34,12 +28,18 @@ class Create extends Action return 'createTable'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_TABLE; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables') - ->httpAlias('/v1/databases/:databaseId/collections') ->desc('Create table') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].create') @@ -49,14 +49,14 @@ class Create extends Action ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'createTable', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-collection.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, - model: UtopiaResponse::MODEL_TABLE, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -65,52 +65,13 @@ class Create extends Action ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $tableId = $tableId === 'unique()' ? ID::unique() : $tableId; - - $permissions = Permission::aggregate($permissions) ?? []; - - try { - $table = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ - '$id' => $tableId, - 'databaseInternalId' => $database->getInternalId(), - 'databaseId' => $databaseId, - '$permissions' => $permissions, - 'documentSecurity' => $documentSecurity, - 'enabled' => $enabled, - 'name' => $name, - 'search' => \implode(' ', [$tableId, $name]), - ])); - - $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); - } catch (DuplicateException) { - throw new Exception(Exception::TABLE_ALREADY_EXISTS); - } catch (LimitException) { - throw new Exception(Exception::TABLE_LIMIT_EXCEEDED); - } - - $queueForEvents - ->setContext('database', $database) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($table, UtopiaResponse::MODEL_TABLE); + ->callback(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $name, $permissions, $documentSecurity, $enabled, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php index b7e8f6ecf7..810edf7f21 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -4,20 +4,19 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\Delete as CollectionDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class Delete extends Action +class Delete extends CollectionDelete { use HTTP; @@ -26,12 +25,18 @@ class Delete extends Action return 'deleteTable'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_TABLE; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId') ->desc('Delete table') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -41,8 +46,8 @@ class Delete extends Action ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'deleteTable', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/delete-collection.md', auth: [AuthType::KEY], responses: [ @@ -59,38 +64,8 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $tableId)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); - } - - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); - - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_COLLECTION) - ->setDatabase($database) - ->setTable($table); - - $queueForEvents - ->setContext('database', $database) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setPayload($response->output($table, UtopiaResponse::MODEL_TABLE)); - - $response->noContent(); + ->callback(function (string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php index 4c76c1f89d..80798a4516 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php @@ -2,20 +2,19 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\Get as CollectionGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class Get extends Action +class Get extends CollectionGet { use HTTP; @@ -24,26 +23,32 @@ class Get extends Action return 'getTable'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_TABLE; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId') ->desc('Get table') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'getTable', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/get-collection.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_TABLE, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -52,23 +57,8 @@ class Get extends Action ->param('tableId', '', new UID(), 'Table ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $response->dynamic($table, UtopiaResponse::MODEL_TABLE); + ->callback(function (string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $tableId, $response, $dbForProject); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php index 1dba2493cf..b3ce8cd272 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php @@ -2,31 +2,24 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables\Logs; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\Logs\XList as CollectionLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use DeviceDetector\DeviceDetector as Detector; use MaxMind\Db\Reader; -use Utopia\Audit\Audit; use Utopia\Database\Database; -use Utopia\Database\DateTime; -use Utopia\Database\Document; -use Utopia\Database\Exception\Query as QueryException; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class XList extends Action +class XList extends CollectionLogXList { use HTTP; @@ -35,26 +28,32 @@ class XList extends Action return 'listTableLogs'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_LOG_LIST; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/logs') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/logs') ->desc('List table logs') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'listTableLogs', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/get-collection-logs.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_LOG_LIST, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -66,88 +65,8 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); - - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - // Temp fix for logs - $queries[] = Query::or([ - Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), - Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), - ]); - - $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/table/' . $tableId; - $logs = $audit->getLogsByResource($resource, $queries); - - $output = []; - - foreach ($logs as $i => &$log) { - $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - - $detector = new Detector($log['userAgent']); - $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - - $os = $detector->getOS(); - $client = $detector->getClient(); - $device = $detector->getDevice(); - - $output[$i] = new Document([ - 'event' => $log['event'], - 'userId' => $log['data']['userId'], - 'userEmail' => $log['data']['userEmail'] ?? null, - 'userName' => $log['data']['userName'] ?? null, - 'mode' => $log['data']['mode'] ?? null, - 'ip' => $log['ip'], - 'time' => $log['time'], - 'osCode' => $os['osCode'], - 'osName' => $os['osName'], - 'osVersion' => $os['osVersion'], - 'clientType' => $client['clientType'], - 'clientCode' => $client['clientCode'], - 'clientName' => $client['clientName'], - 'clientVersion' => $client['clientVersion'], - 'clientEngine' => $client['clientEngine'], - 'clientEngineVersion' => $client['clientEngineVersion'], - 'deviceName' => $device['deviceName'], - 'deviceBrand' => $device['deviceBrand'], - 'deviceModel' => $device['deviceModel'] - ]); - - $record = $geodb->get($log['ip']); - - if ($record) { - $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; - $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); - } else { - $output[$i]['countryCode'] = '--'; - $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); - } - } - - $response->dynamic(new Document([ - 'total' => $audit->countLogsByResource($resource, $queries), - 'logs' => $output, - ]), UtopiaResponse::MODEL_LOG_LIST); + ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb) { + parent::action($databaseId, $tableId, $queries, $response, $dbForProject, $locale, $geodb); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php index f5621feab3..fde9114e59 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -3,24 +3,22 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\Update as CollectionUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; -class Update extends Action +class Update extends CollectionUpdate { use HTTP; @@ -29,12 +27,18 @@ class Update extends Action return 'updateTable'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_TABLE; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId') ->desc('Update table') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -44,8 +48,8 @@ class Update extends Action ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'updateTable', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-collection.md', auth: [AuthType::KEY], responses: [ @@ -65,46 +69,8 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $permissions ??= $table->getPermissions() ?? []; - - // Map aggregate permissions into the multiple permissions they represent. - $permissions = Permission::aggregate($permissions); - - $enabled ??= $table->getAttribute('enabled', true); - - $table = $dbForProject->updateDocument( - 'database_' . $database->getInternalId(), - $tableId, - $table - ->setAttribute('name', $name) - ->setAttribute('$permissions', $permissions) - ->setAttribute('documentSecurity', $documentSecurity) - ->setAttribute('enabled', $enabled) - ->setAttribute('search', \implode(' ', [$tableId, $name])) - ); - - $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $permissions, $documentSecurity); - - $queueForEvents - ->setContext('database', $database) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()); - - $response->dynamic($table, UtopiaResponse::MODEL_TABLE); + ->callback(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $name, $permissions, $documentSecurity, $enabled, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php index b528fee7cf..9ba99d32e1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -2,24 +2,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables\Usage; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\Usage\Get as CollectionUsageGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Config\Config; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; -class Get extends Action +class Get extends CollectionUsageGet { use HTTP; @@ -28,12 +24,18 @@ class Get extends Action return 'getTableUsage'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_USAGE_TABLE; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/usage') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/usage') ->desc('Get table usage stats') ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') @@ -41,13 +43,13 @@ class Get extends Action ->label('sdk', new Method( namespace: 'databases', group: null, - name: 'getTableUsage', + name: self::getName(), description: '/docs/references/databases/get-collection-usage.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_USAGE_TABLE, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON, @@ -57,75 +59,8 @@ class Get extends Action ->param('tableId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $range, string $tableId, UtopiaResponse $response, Database $dbForProject): void - { - - $database = $dbForProject->getDocument('databases', $databaseId); - $tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId()); - - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $tableDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', ['inf']) - ]); - - $stats[$metric]['total'] = $result['value'] ?? 0; - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('metric', [$metric]), - Query::equal('period', [$period]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric]['data'] = []; - foreach ($results as $result) { - $stats[$metric]['data'][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric]['total'] = $stats[$metric]['total']; - $usage[$metric]['data'] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric]['data'][] = [ - 'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0, - 'date' => $formatDate, - ]; - } - } - - $response->dynamic(new Document([ - 'range' => $range, - 'rows' => $usage[$metrics[0]]['data'], - 'rowsTotal' => $usage[$metrics[0]]['total'], - ]), UtopiaResponse::MODEL_USAGE_TABLE); + ->callback(function (string $databaseId, string $range, string $tableId, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $range, $tableId, $response, $dbForProject); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index e6d2a2d02a..f90a1f66f2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -2,7 +2,8 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Collections\Action; +use Appwrite\Platform\Modules\Databases\Http\Collections\XList as CollectionXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -10,18 +11,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Tables; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\Order as OrderException; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Text; -class XList extends Action +class XList extends CollectionXList { use HTTP; @@ -30,26 +25,32 @@ class XList extends Action return 'listTables'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_TABLE_LIST; + } + public function __construct() { + $this->setContext(Action::TABLE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables') - ->httpAlias('/v1/databases/:databaseId/collections') ->desc('List tables') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'listTables', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/list-collections.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_TABLE_LIST, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -59,59 +60,8 @@ class XList extends Action ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $queries = Query::parseQueries($queries); - - if (!empty($search)) { - $queries[] = Query::search('search', $search); - } - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - $cursor = reset($cursor); - - if ($cursor) { - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $tableId = $cursor->getValue(); - $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($cursorDocument->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Table '{$tableId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument); - } - - $filterQueries = Query::groupByType($queries)['filters']; - - try { - $tables = $dbForProject->find('database_' . $database->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); - } - - $response->dynamic(new Document([ - 'tables' => $tables, - 'total' => $total, - ]), UtopiaResponse::MODEL_TABLE_LIST); + ->callback(function (string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $queries, $search, $response, $dbForProject); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index ffdd4f1903..26a4aa0942 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -2,6 +2,13 @@ namespace Appwrite\Platform\Modules\Databases\Services; +use Appwrite\Platform\Modules\Databases\Http\Collections\Create as CreateCollection; +use Appwrite\Platform\Modules\Databases\Http\Collections\Delete as DeleteCollection; +use Appwrite\Platform\Modules\Databases\Http\Collections\Get as GetCollection; +use Appwrite\Platform\Modules\Databases\Http\Collections\Logs\XList as ListCollectionLogs; +use Appwrite\Platform\Modules\Databases\Http\Collections\Update as UpdateCollection; +use Appwrite\Platform\Modules\Databases\Http\Collections\Usage\Get as GetCollectionUsage; +use Appwrite\Platform\Modules\Databases\Http\Collections\XList as ListCollections; use Appwrite\Platform\Modules\Databases\Http\Columns\Boolean\Create as CreateBoolean; use Appwrite\Platform\Modules\Databases\Http\Columns\Boolean\Update as UpdateBoolean; use Appwrite\Platform\Modules\Databases\Http\Columns\Datetime\Create as CreateDatetime; @@ -59,7 +66,7 @@ class Http extends Service $this->type = Service::TYPE_HTTP; $this->registerDatabaseActions(); - $this->registerTableActions(); + $this->registerCollectionAndTableActions(); $this->registerColumnActions(); $this->registerIndexActions(); $this->registerRowActions(); @@ -77,8 +84,18 @@ class Http extends Service $this->addAction(ListDatabaseUsage::getName(), new ListDatabaseUsage()); } - private function registerTableActions(): void + private function registerCollectionAndTableActions(): void { + // Collections + $this->addAction(CreateCollection::getName(), new CreateCollection()); + $this->addAction(GetCollection::getName(), new GetCollection()); + $this->addAction(UpdateCollection::getName(), new UpdateCollection()); + $this->addAction(DeleteCollection::getName(), new DeleteCollection()); + $this->addAction(ListCollections::getName(), new ListCollections()); + $this->addAction(ListCollectionLogs::getName(), new ListCollectionLogs()); + $this->addAction(GetCollectionUsage::getName(), new GetCollectionUsage()); + + // Tables $this->addAction(CreateTable::getName(), new CreateTable()); $this->addAction(GetTable::getName(), new GetTable()); $this->addAction(UpdateTable::getName(), new UpdateTable()); From 69cc4f0bb6d6f5b6f6f99c559612e919fa23d514 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 17:32:54 +0530 Subject: [PATCH 059/173] update: handle the context logic, use defaults. --- app/init/constants.php | 10 +++++++ .../Databases/Http/Collections/Action.php | 30 ++++--------------- .../Databases/Http/Collections/Create.php | 4 --- .../Databases/Http/Collections/Delete.php | 4 --- .../Databases/Http/Collections/Get.php | 4 --- .../Databases/Http/Collections/Logs/XList.php | 4 --- .../Databases/Http/Collections/Update.php | 4 --- .../Databases/Http/Collections/Usage/Get.php | 4 --- .../Databases/Http/Collections/XList.php | 4 --- .../Modules/Databases/Http/Tables/Create.php | 2 +- .../Modules/Databases/Http/Tables/Delete.php | 2 +- .../Modules/Databases/Http/Tables/Get.php | 2 +- .../Databases/Http/Tables/Logs/XList.php | 2 +- .../Modules/Databases/Http/Tables/Update.php | 2 +- .../Databases/Http/Tables/Usage/Get.php | 2 +- .../Modules/Databases/Http/Tables/XList.php | 2 +- 16 files changed, 22 insertions(+), 60 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index 2b15f9fa0b..80d4796aad 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -257,3 +257,13 @@ const RESOURCE_TYPE_PROVIDERS = 'providers'; const RESOURCE_TYPE_TOPICS = 'topics'; const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers'; const RESOURCE_TYPE_MESSAGES = 'messages'; + +// Context constants for database + +const DATABASE_ROWS_CONTEXT = 'row'; +const DATABASE_TABLES_CONTEXT = 'table'; +const DATABASE_COLUMNS_CONTEXT = 'column'; + +const DATABASE_DOCUMENTS_CONTEXT = 'document'; +const DATABASE_ATTRIBUTES_CONTEXT = 'attribute'; +const DATABASE_COLLECTIONS_CONTEXT = 'collection'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php index d2129f31ea..d00bc1f9f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php @@ -13,26 +13,21 @@ use Utopia\Platform\Action as UtopiaAction; */ abstract class Action extends UtopiaAction { - /** - * Valid context identifiers. - */ - public const TABLE = 'table'; - public const COLLECTION = 'collection'; /** * The current API context (either 'table' or 'collection'). */ - private ?string $context = null; + private ?string $context = DATABASE_COLLECTIONS_CONTEXT; /** * Set the current API context. * - * @param string $context Must be either `self::TABLE` or `self::COLLECTION`. + * @param string $context Must be either `DATABASE_TABLES_CONTEXT` or `DATABASE_COLLECTIONS_CONTEXT`. */ final protected function setContext(string $context): void { - if (!\in_array($context, [self::TABLE, self::COLLECTION], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Must be either `Action::TABLE` or `Action::COLLECTION`."); + if (!\in_array($context, [DATABASE_TABLES_CONTEXT, DATABASE_COLLECTIONS_CONTEXT], true)) { + throw new \InvalidArgumentException("Invalid context '$context'. Must be either `DATABASE_TABLES_CONTEXT` or `DATABASE_COLLECTIONS_CONTEXT`."); } $this->context = $context; @@ -45,10 +40,6 @@ abstract class Action extends UtopiaAction */ final protected function getContext(): string { - if ($this->context === null) { - throw new \Exception('Missing context: you must call setContext() with either `Action::TABLE` or `Action::COLLECTION` before using this method.'); - } - return $this->context; } @@ -70,7 +61,7 @@ abstract class Action extends UtopiaAction */ final protected function isCollectionsAPI(): bool { - return $this->getContext() === self::COLLECTION; + return $this->getContext() === DATABASE_COLLECTIONS_CONTEXT; } /** @@ -110,15 +101,4 @@ abstract class Action extends UtopiaAction ? Exception::COLLECTION_LIMIT_EXCEEDED : Exception::TABLE_LIMIT_EXCEEDED; } - - /** - * Ensures that a valid context has been set. - * - * @throws \Exception if context is missing - */ - final protected function validateContext(): void - { - $this->getContext(); // Triggers exception if not set - } - } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php index d0320c6dd0..063ce75720 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Create.php @@ -40,8 +40,6 @@ class Create extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections') @@ -80,8 +78,6 @@ class Create extends Action public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void { - $this->validateContext(); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php index d30c37efed..7cec4addea 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Delete.php @@ -32,8 +32,6 @@ class Delete extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId') @@ -69,8 +67,6 @@ class Delete extends Action public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void { - $this->validateContext(); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php index 962ad3d21d..e7f02b8930 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Get.php @@ -30,8 +30,6 @@ class Get extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId') @@ -62,8 +60,6 @@ class Get extends Action public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject): void { - $this->validateContext(); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php index 429c39dc44..bd31ad2fe1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Logs/XList.php @@ -42,8 +42,6 @@ class XList extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/logs') @@ -77,8 +75,6 @@ class XList extends Action public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void { - $this->validateContext(); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php index 582f0e84a3..8c8dfb58b5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Update.php @@ -35,8 +35,6 @@ class Update extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId') @@ -75,8 +73,6 @@ class Update extends Action public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void { - $this->validateContext(); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php index a9a924114f..60c7af4420 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Usage/Get.php @@ -35,8 +35,6 @@ class Get extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/usage') @@ -68,8 +66,6 @@ class Get extends Action public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject): void { - $this->validateContext(); - $database = $dbForProject->getDocument('databases', $databaseId); $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php index e540e86c06..98246defaa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php @@ -36,8 +36,6 @@ class XList extends Action public function __construct() { - $this->setContext(Action::COLLECTION); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections') @@ -69,8 +67,6 @@ class XList extends Action public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void { - $this->validateContext(); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php index 6f30c897b4..8a6c009e87 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php @@ -35,7 +35,7 @@ class Create extends CollectionCreate public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php index 810edf7f21..e06c19b308 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -32,7 +32,7 @@ class Delete extends CollectionDelete public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php index 80798a4516..e5f2954de5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php @@ -30,7 +30,7 @@ class Get extends CollectionGet public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php index b3ce8cd272..67ff31efb7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php @@ -35,7 +35,7 @@ class XList extends CollectionLogXList public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php index fde9114e59..f27232353e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -34,7 +34,7 @@ class Update extends CollectionUpdate public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php index 9ba99d32e1..647a98d952 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -31,7 +31,7 @@ class Get extends CollectionUsageGet public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index f90a1f66f2..77eb556301 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -32,7 +32,7 @@ class XList extends CollectionXList public function __construct() { - $this->setContext(Action::TABLE); + $this->setContext(DATABASE_TABLES_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) From bc8a38ba55c05c17d4e308fa3ed8585d5fc4c19f Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 7 May 2025 20:01:46 +0530 Subject: [PATCH 060/173] update: abstraction over attribute and column apis [WIP]. --- .../Databases/Http/Attributes/Action.php | 627 ++++++++++++++++++ .../Http/Attributes/Boolean/Create.php | 94 +++ .../Http/Attributes/Boolean/Update.php | 95 +++ .../Http/Attributes/Datetime/Create.php | 104 +++ .../Http/Attributes/Datetime/Update.php | 97 +++ .../Databases/Http/Attributes/Delete.php | 161 +++++ .../Http/Attributes/Email/Create.php | 104 +++ .../Http/Attributes/Email/Update.php | 98 +++ .../Databases/Http/Attributes/Enum/Create.php | 113 ++++ .../Databases/Http/Attributes/Enum/Update.php | 102 +++ .../Http/Attributes/Float/Create.php | 121 ++++ .../Http/Attributes/Float/Update.php | 109 +++ .../Modules/Databases/Http/Attributes/Get.php | 101 +++ .../Databases/Http/Attributes/IP/Create.php | 104 +++ .../Databases/Http/Attributes/IP/Update.php | 98 +++ .../Http/Attributes/Integer/Create.php | 123 ++++ .../Http/Attributes/Integer/Update.php | 109 +++ .../Http/Attributes/Relationship/Create.php | 169 +++++ .../Http/Attributes/Relationship/Update.php | 103 +++ .../Http/Attributes/String/Create.php | 122 ++++ .../Http/Attributes/String/Update.php | 102 +++ .../Databases/Http/Attributes/URL/Create.php | 96 +++ .../Databases/Http/Attributes/URL/Update.php | 98 +++ .../Databases/Http/Attributes/XList.php | 130 ++++ .../Databases/Http/Collections/Action.php | 1 - .../Http/Columns/Datetime/Create.php | 2 +- .../Databases/Http/Columns/String/Update.php | 2 +- .../Modules/Databases/Http/Tables/Create.php | 1 - .../Modules/Databases/Http/Tables/Delete.php | 1 - .../Modules/Databases/Http/Tables/Get.php | 1 - .../Databases/Http/Tables/Logs/XList.php | 1 - .../Modules/Databases/Http/Tables/Update.php | 1 - .../Databases/Http/Tables/Usage/Get.php | 1 - .../Modules/Databases/Http/Tables/XList.php | 1 - 34 files changed, 3182 insertions(+), 10 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php new file mode 100644 index 0000000000..66e8313d33 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php @@ -0,0 +1,627 @@ +context = $context; + } + + /** + * Get the current context. + * + * @throws \LogicException If context has not been set. + */ + final protected function getContext(): string + { + return $this->context; + } + + /** + * Returns true if current context is Collections API. + */ + final protected function isCollectionsAPI(): bool + { + // columns in tables context + // attributes in collections context + return $this->getContext() === DATABASE_ATTRIBUTES_CONTEXT; + } + + /** + * Get the SDK group name for the current action. + * + * Can be used for XList operations as well! + */ + final protected function getSdkGroup(): string + { + return $this->isCollectionsAPI() ? 'attributes' : 'columns'; + } + + /** + * Get the correct parent param key (e.g. `tableId` or `collectionId`) + */ + final protected function getParentEventsParamKey(): string + { + return $this->isCollectionsAPI() ? 'collectionId' : 'tableId'; + } + + /** + * Get the correct param key (e.g. `attributeId` or `columnId`) + */ + final protected function getEventsParamKey(): string + { + return $this->getContext() . 'Id'; + } + + /** + * Set the correct response model. + */ + final protected function setResponseModel(string|array $model): void + { + $this->responseModel = $model; + } + + /** + * Get the correct response model. + */ + final protected function getResponseModel(): string|array + { + if ($this->responseModel === null) { + throw new \LogicException("Missing response model: you must call setResponseModel() before using it."); + } + + return $this->responseModel; + } + + /** + * Get the appropriate parent level not found exception. + */ + final protected function getParentNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_NOT_FOUND + : Exception::TABLE_NOT_FOUND; + } + + /** + * Get the appropriate not found exception. + */ + final protected function getNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_NOT_FOUND + : Exception::COLUMN_NOT_FOUND; + } + + /** + * Get the appropriate already exists exception. + */ + final protected function getDuplicateException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_ALREADY_EXISTS + : Exception::COLUMN_ALREADY_EXISTS; + } + + /** + * Get the appropriate limit exceeded exception. + */ + final protected function getLimitException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_LIMIT_EXCEEDED + : Exception::COLUMN_LIMIT_EXCEEDED; + } + + /** + * Get the correct default unsupported message. + */ + final protected function getDefaultUnsupportedException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED + : Exception::COLUMN_DEFAULT_UNSUPPORTED; + } + + /** + * Get the correct format unsupported message. + */ + final protected function getFormatUnsupportedException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_FORMAT_UNSUPPORTED + : Exception::COLUMN_FORMAT_UNSUPPORTED; + } + + /** + * Get the exception for invalid type or format mismatch. + */ + final protected function getTypeInvalidException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_TYPE_INVALID + : Exception::COLUMN_TYPE_INVALID; + } + + /** + * Get the exception for resizing invalid attributes/columns. + */ + final protected function getInvalidResizeException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_INVALID_RESIZE + : Exception::COLUMN_INVALID_RESIZE; + } + + /** + * Get the exception for invalid attributes/columns value. + */ + final protected function getInvalidValueException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_VALUE_INVALID + : Exception::COLUMN_VALUE_INVALID; + } + + /** + * Get the exception for non-available column/attribute. + */ + final protected function getNotAvailableException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_NOT_AVAILABLE + : Exception::COLUMN_NOT_AVAILABLE; + } + + /** + * Get the proper column/attribute type based on set context. + */ + final protected function getCorrectModel(string $type, string $format): string + { + $isCollections = $this->isCollectionsAPI(); + + return match ($type) { + Database::VAR_BOOLEAN => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN + : UtopiaResponse::MODEL_COLUMN_BOOLEAN, + + Database::VAR_INTEGER => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_INTEGER + : UtopiaResponse::MODEL_COLUMN_INTEGER, + + Database::VAR_FLOAT => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_FLOAT + : UtopiaResponse::MODEL_COLUMN_FLOAT, + + Database::VAR_DATETIME => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_DATETIME + : UtopiaResponse::MODEL_COLUMN_DATETIME, + + Database::VAR_RELATIONSHIP => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP + : UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, + + Database::VAR_STRING => match ($format) { + APP_DATABASE_ATTRIBUTE_EMAIL => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_EMAIL + : UtopiaResponse::MODEL_COLUMN_EMAIL, + + APP_DATABASE_ATTRIBUTE_ENUM => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_ENUM + : UtopiaResponse::MODEL_COLUMN_ENUM, + + APP_DATABASE_ATTRIBUTE_IP => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_IP + : UtopiaResponse::MODEL_COLUMN_IP, + + APP_DATABASE_ATTRIBUTE_URL => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_URL + : UtopiaResponse::MODEL_COLUMN_URL, + + default => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE_STRING + : UtopiaResponse::MODEL_COLUMN_STRING, + }, + default => $isCollections + ? UtopiaResponse::MODEL_ATTRIBUTE + : UtopiaResponse::MODEL_COLUMN, + }; + } + + final protected function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document + { + $key = $attribute->getAttribute('key'); + $type = $attribute->getAttribute('type', ''); + $size = $attribute->getAttribute('size', 0); + $required = $attribute->getAttribute('required', true); + $signed = $attribute->getAttribute('signed', true); // integers are signed by default + $array = $attribute->getAttribute('array', false); + $format = $attribute->getAttribute('format', ''); + $formatOptions = $attribute->getAttribute('formatOptions', []); + $filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint + $default = $attribute->getAttribute('default'); + $options = $attribute->getAttribute('options', []); + + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + if (!empty($format)) { + if (!Structure::hasFormat($format, $type)) { + throw new Exception($this->getFormatUnsupportedException(), "Format {$format} not available for {$type} columns."); + } + } + + // Must throw here since dbForProject->createAttribute is performed by db worker + if ($required && isset($default)) { + throw new Exception($this->getDefaultUnsupportedException(), 'Cannot set default value for required ' . $this->getContext()); + } + + if ($array && isset($default)) { + throw new Exception($this->getDefaultUnsupportedException(), 'Cannot set default value for array ' . $this->getContext() . 's'); + } + + if ($type === Database::VAR_RELATIONSHIP) { + $options['side'] = Database::RELATION_SIDE_PARENT; + $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); + if ($relatedTable->isEmpty()) { + $parent = $this->isCollectionsAPI() ? 'collection' : 'table'; + throw new Exception($this->getParentNotFoundException(), "The related $parent was not found."); + } + } + + try { + $attribute = new Document([ + '$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + 'key' => $key, + 'databaseInternalId' => $db->getInternalId(), + 'databaseId' => $db->getId(), + 'collectionInternalId' => $collection->getInternalId(), + 'collectionId' => $collectionId, + 'type' => $type, + 'status' => 'processing', // processing, available, failed, deleting, stuck + 'size' => $size, + 'required' => $required, + 'signed' => $signed, + 'default' => $default, + 'array' => $array, + 'format' => $format, + 'formatOptions' => $formatOptions, + 'filters' => $filters, + 'options' => $options, + ]); + + $dbForProject->checkAttribute($collection, $attribute); + $attribute = $dbForProject->createDocument('attributes', $attribute); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (LimitException) { + throw new Exception($this->getLimitException()); + } catch (Throwable $e) { + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + throw $e; + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + + if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { + $twoWayKey = $options['twoWayKey']; + $options['relatedCollection'] = $collection->getId(); + $options['twoWayKey'] = $key; + $options['side'] = Database::RELATION_SIDE_CHILD; + + try { + $twoWayAttribute = new Document([ + '$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey), + 'key' => $twoWayKey, + 'databaseInternalId' => $db->getInternalId(), + 'databaseId' => $db->getId(), + 'collectionInternalId' => $relatedTable->getInternalId(), + 'collectionId' => $relatedTable->getId(), + 'type' => $type, + 'status' => 'processing', // processing, available, failed, deleting, stuck + 'size' => $size, + 'required' => $required, + 'signed' => $signed, + 'default' => $default, + 'array' => $array, + 'format' => $format, + 'formatOptions' => $formatOptions, + 'filters' => $filters, + 'options' => $options, + ]); + + $dbForProject->checkAttribute($relatedTable, $twoWayAttribute); + $dbForProject->createDocument('attributes', $twoWayAttribute); + } catch (DuplicateException) { + $dbForProject->deleteDocument('attributes', $attribute->getId()); + throw new Exception($this->getDuplicateException()); + } catch (LimitException) { + $dbForProject->deleteDocument('attributes', $attribute->getId()); + throw new Exception($this->getLimitException()); + } catch (Throwable $e) { + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + throw $e; + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + } + + $queueForDatabase + ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) + ->setDatabase($db); + + if ($this->isCollectionsAPI()) { + $queueForDatabase + ->setDocument($attribute) + ->setCollection($collection); + } else { + $queueForDatabase + ->setRow($attribute) + ->setTable($collection); + } + + $queueForEvents + ->setContext('database', $db) + ->setParam('databaseId', $databaseId) + ->setParam($this->getEventsParamKey(), $attribute->getId()) + // tableId or columnId + ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + + $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); + + return $attribute; + } + + final protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document + { + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + + if ($attribute->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + if ($attribute->getAttribute('status') !== 'available') { + throw new Exception($this->getNotAvailableException()); + } + + if ($attribute->getAttribute(('type') !== $type)) { + throw new Exception($this->getTypeInvalidException()); + } + + if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) { + throw new Exception($this->getTypeInvalidException()); + } + + if ($required && isset($default)) { + throw new Exception($this->getDefaultUnsupportedException(), 'Cannot set default value for required ' . $this->getContext()); + } + + if ($attribute->getAttribute('array', false) && isset($default)) { + throw new Exception($this->getDefaultUnsupportedException(), 'Cannot set default value for array ' . $this->getContext() . 's'); + } + + $collectionId = 'database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId(); + + $attribute + ->setAttribute('default', $default) + ->setAttribute('required', $required); + + if (!empty($size)) { + $attribute->setAttribute('size', $size); + } + + switch ($attribute->getAttribute('format')) { + case APP_DATABASE_ATTRIBUTE_INT_RANGE: + case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE: + $min ??= $attribute->getAttribute('formatOptions')['min']; + $max ??= $attribute->getAttribute('formatOptions')['max']; + + if ($min > $max) { + throw new Exception($this->getTypeInvalidException(), 'Minimum value must be lesser than maximum value'); + } + + if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { + $validator = new Range($min, $max, Database::VAR_INTEGER); + } else { + $validator = new Range($min, $max, Database::VAR_FLOAT); + + if (!is_null($default)) { + $default = \floatval($default); + } + } + + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($this->getTypeInvalidException(), $validator->getDescription()); + } + + $options = [ + 'min' => $min, + 'max' => $max + ]; + $attribute->setAttribute('formatOptions', $options); + + break; + case APP_DATABASE_ATTRIBUTE_ENUM: + if (empty($elements)) { + throw new Exception($this->getTypeInvalidException(), 'Enum elements must not be empty'); + } + + foreach ($elements as $element) { + if (\strlen($element) === 0) { + throw new Exception($this->getTypeInvalidException(), 'Each enum element must not be empty'); + } + } + + if (!is_null($default) && !in_array($default, $elements)) { + throw new Exception($this->getTypeInvalidException(), 'Default value not found in elements'); + } + + $options = [ + 'elements' => $elements + ]; + + $attribute->setAttribute('formatOptions', $options); + + break; + } + + if ($type === Database::VAR_RELATIONSHIP) { + $primaryDocumentOptions = \array_merge($attribute->getAttribute('options', []), $options); + $attribute->setAttribute('options', $primaryDocumentOptions); + try { + $dbForProject->updateRelationship( + collection: $collectionId, + id: $key, + newKey: $newKey, + onDelete: $primaryDocumentOptions['onDelete'], + ); + } catch (NotFoundException) { + throw new Exception($this->getNotFoundException()); + } + + if ($primaryDocumentOptions['twoWay']) { + $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']); + + $relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']); + + if (!empty($newKey) && $newKey !== $key) { + $options['twoWayKey'] = $newKey; + } + + $relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options); + $relatedAttribute->setAttribute('options', $relatedOptions); + $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute); + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); + } + } else { + try { + $dbForProject->updateAttribute( + collection: $collectionId, + id: $key, + size: $size, + required: $required, + default: $default, + formatOptions: $options, + newKey: $newKey ?? null + ); + } catch (TruncateException) { + throw new Exception($this->getInvalidResizeException()); + } catch (NotFoundException) { + throw new Exception($this->getNotFoundException()); + } catch (LimitException) { + throw new Exception($this->getLimitException()); + } catch (IndexException $e) { + throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); + } + } + + if (!empty($newKey) && $key !== $newKey) { + $originalUid = $attribute->getId(); + + $attribute + ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey)) + ->setAttribute('key', $newKey); + + $dbForProject->updateDocument('attributes', $originalUid, $attribute); + + /** + * @var Document $index + */ + foreach ($collection->getAttribute('indexes') as $index) { + /** + * @var string[] $attribute + */ + $attribute = $index->getAttribute('attributes', []); + $found = \array_search($key, $attribute); + + if ($found !== false) { + $attribute[$found] = $newKey; + $index->setAttribute('attributes', $attribute); + $dbForProject->updateDocument('indexes', $index->getId(), $index); + } + } + } else { + $attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId()); + + $queueForEvents + ->setContext('database', $db) + ->setParam('databaseId', $databaseId) + ->setParam($this->getEventsParamKey(), $attribute->getId()) + // tableId or columnId + ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + + return $attribute; + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php new file mode 100644 index 0000000000..583e42b4ed --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php @@ -0,0 +1,94 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); + + $this->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') + ->desc('Create boolean attribute') + ->groups(['api', 'database', 'schema']) + ->label('event', 'databases.[databaseId].collections.[collectionId].collections.[attributeId].create') + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-boolean-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Boolean(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?bool $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + + $attribute = $this->createAttribute($databaseId, $collectionId, new Document([ + 'key' => $key, + 'type' => Database::VAR_BOOLEAN, + 'size' => 0, + 'required' => $required, + 'default' => $default, + 'array' => $array, + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php new file mode 100644 index 0000000000..41a0c5a621 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php @@ -0,0 +1,95 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); + + $this->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') + ->desc('Update boolean attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-boolean-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') + ->param('newKey', null, new Key(), 'New attribute key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?bool $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_BOOLEAN, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php new file mode 100644 index 0000000000..40d7aed1e9 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php @@ -0,0 +1,104 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') + ->desc('Create datetime attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-datetime-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel() + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true, ['dbForProject']) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $attribute = $this->createAttribute( + $databaseId, + $collectionId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_DATETIME, + 'size' => 0, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'filters' => ['datetime'], + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php new file mode 100644 index 0000000000..386d6682ad --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php @@ -0,0 +1,97 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') + ->desc('Update datetime attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-datetime-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel() + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for attribute when not provided. Cannot be set when attribute is required.', injections: ['dbForProject']) + ->param('newKey', null, new Key(), 'New attribute key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_DATETIME, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php new file mode 100644 index 0000000000..da29fd09ee --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php @@ -0,0 +1,161 @@ +setResponseModel(UtopiaResponse::MODEL_NONE); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') + ->desc('Delete attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: 'attributes', + name: 'deleteAttribute', + description: '/docs/references/databases/delete-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($db->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + if ($attribute->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + $validator = new IndexDependencyValidator( + $collection->getAttribute('indexes'), + $dbForProject->getAdapter()->getSupportForCastIndexArray(), + ); + if (!$validator->isValid($attribute)) { + throw new Exception(Exception::INDEX_DEPENDENCY); + } + + if ($attribute->getAttribute('status') === 'available') { + $attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting')); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + + if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { + $options = $attribute->getAttribute('options'); + if ($options['twoWay']) { + $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); + if ($relatedCollection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + if ($relatedAttribute->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + if ($relatedAttribute->getAttribute('status') === 'available') { + $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'deleting')); + } + + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + } + } + + $queueForDatabase + ->setDatabase($db) + ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE); + + if ($this->isCollectionsAPI()) { + $queueForDatabase + ->setRow($attribute) + ->setTable($collection); + } else { + $queueForDatabase + ->setDocument($attribute) + ->setCollection($collection); + } + + $type = $attribute->getAttribute('type'); + $format = $attribute->getAttribute('format'); + + $model = $this->getCorrectModel($type, $format); + + $queueForEvents + ->setContext('database', $db) + ->setParam('databaseId', $databaseId) + ->setPayload($response->output($attribute, $model)) + ->setParam($this->getEventsParamKey(), $attribute->getId()) + // tableId or columnId + ->setParam($this->getParentEventsParamKey(), $collection->getId()) + // set proper context + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php new file mode 100644 index 0000000000..9b63f2198a --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php @@ -0,0 +1,104 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/email') + ->desc('Create email attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-email-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $attribute = $this->createAttribute( + $databaseId, + $tableId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => 254, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php new file mode 100644 index 0000000000..0c41961099 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php @@ -0,0 +1,98 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') + ->desc('Update email attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-email-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_EMAIL, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php new file mode 100644 index 0000000000..61298cbe94 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php @@ -0,0 +1,113 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_ENUM); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') + ->desc('Create enum attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-attribute-enum.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('elements', [], new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of enum values.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + array $elements, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + if (!is_null($default) && !\in_array($default, $elements, true)) { + throw new Exception($this->getInvalidValueException(), 'Default value not found in elements'); + } + + $attribute = $this->createAttribute( + $databaseId, + $collectionId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_ENUM, + 'formatOptions' => ['elements' => $elements], + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php new file mode 100644 index 0000000000..37d8065608 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php @@ -0,0 +1,102 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_ENUM); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') + ->desc('Update enum attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-enum-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('elements', null, new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Updated list of enum values.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?array $elements, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_ENUM, + default: $default, + required: $required, + elements: $elements, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php new file mode 100644 index 0000000000..9603cf4c9a --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php @@ -0,0 +1,121 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/float') + ->desc('Create float attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-float-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('min', null, new FloatValidator(), 'Minimum value.', true) + ->param('max', null, new FloatValidator(), 'Maximum value.', true) + ->param('default', null, new FloatValidator(), 'Default value. Cannot be set when required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?float $min, + ?float $max, + ?float $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $min ??= -PHP_FLOAT_MAX; + $max ??= PHP_FLOAT_MAX; + + if ($min > $max) { + throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value'); + } + + $validator = new Range($min, $max, Database::VAR_FLOAT); + if (!\is_null($default) && !$validator->isValid($default)) { + throw new Exception($this->getInvalidValueException(), $validator->getDescription()); + } + + $attribute = $this->createAttribute($databaseId, $collectionId, new Document([ + 'key' => $key, + 'type' => Database::VAR_FLOAT, + 'size' => 0, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, + 'formatOptions' => ['min' => $min, 'max' => $max], + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $formatOptions = $attribute->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $attribute->setAttribute('min', \floatval($formatOptions['min'])); + $attribute->setAttribute('max', \floatval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php new file mode 100644 index 0000000000..012e8239c9 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php @@ -0,0 +1,109 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') + ->desc('Update float attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-float-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('min', null, new FloatValidator(), 'Minimum value.', true) + ->param('max', null, new FloatValidator(), 'Maximum value.', true) + ->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.') + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?float $min, + ?float $max, + ?float $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_FLOAT, + default: $default, + required: $required, + min: $min, + max: $max, + newKey: $newKey + ); + + $formatOptions = $attribute->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $attribute->setAttribute('min', \floatval($formatOptions['min'])); + $attribute->setAttribute('max', \floatval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php new file mode 100644 index 0000000000..e782897be2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php @@ -0,0 +1,101 @@ +setResponseModel([ + UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, + UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, + UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, + UtopiaResponse::MODEL_ATTRIBUTE_EMAIL, + UtopiaResponse::MODEL_ATTRIBUTE_ENUM, + UtopiaResponse::MODEL_ATTRIBUTE_URL, + UtopiaResponse::MODEL_ATTRIBUTE_IP, + UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, + UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, + UtopiaResponse::MODEL_ATTRIBUTE_STRING, + ]); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') + ->desc('Get column') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'columns', + name: 'getColumn', + description: '/docs/references/databases/get-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel() + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('key', '', new Key(), 'Column Key.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $tableId, + string $key, + UtopiaResponse $response, + Database $dbForProject + ): void { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + if ($attribute->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + foreach ($attribute->getAttribute('options', []) as $optKey => $optVal) { + $attribute->setAttribute($optKey, $optVal); + } + + $type = $attribute->getAttribute('type'); + $format = $attribute->getAttribute('format'); + $model = $this->getCorrectModel($type, $format); + + $response->dynamic($attribute, $model); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php new file mode 100644 index 0000000000..8176b03bba --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php @@ -0,0 +1,104 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_IP); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') + ->desc('Create IP address attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-ip-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new IP(), 'Default value. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $attribute = $this->createAttribute( + $databaseId, + $collectionId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => 39, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_IP, + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php new file mode 100644 index 0000000000..868b735bbb --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php @@ -0,0 +1,98 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_IP); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') + ->desc('Update IP address attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-ip-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when attribute is required.') + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_IP, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php new file mode 100644 index 0000000000..99371b26d9 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php @@ -0,0 +1,123 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') + ->desc('Create integer attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-integer-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('min', null, new Integer(), 'Minimum value', true) + ->param('max', null, new Integer(), 'Maximum value', true) + ->param('default', null, new Integer(), 'Default value. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?int $min, + ?int $max, + ?int $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $min ??= \PHP_INT_MIN; + $max ??= \PHP_INT_MAX; + + if ($min > $max) { + throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value'); + } + + $validator = new Range($min, $max, Database::VAR_INTEGER); + if (!\is_null($default) && !$validator->isValid($default)) { + throw new Exception($this->getInvalidValueException(), $validator->getDescription()); + } + + $size = $max > 2147483647 ? 8 : 4; + + $attribute = $this->createAttribute($databaseId, $collectionId, new Document([ + 'key' => $key, + 'type' => Database::VAR_INTEGER, + 'size' => $size, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE, + 'formatOptions' => ['min' => $min, 'max' => $max], + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $formatOptions = $attribute->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $attribute->setAttribute('min', \intval($formatOptions['min'])); + $attribute->setAttribute('max', \intval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php new file mode 100644 index 0000000000..51c72af363 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php @@ -0,0 +1,109 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') + ->desc('Update integer attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-integer-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('min', null, new Integer(), 'Minimum value', true) + ->param('max', null, new Integer(), 'Maximum value', true) + ->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when attribute is required.') + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?int $min, + ?int $max, + ?int $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_INTEGER, + default: $default, + required: $required, + min: $min, + max: $max, + newKey: $newKey + ); + + $formatOptions = $attribute->getAttribute('formatOptions', []); + if (!empty($formatOptions)) { + $attribute->setAttribute('min', \intval($formatOptions['min'])); + $attribute->setAttribute('max', \intval($formatOptions['max'])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php new file mode 100644 index 0000000000..cdc1861c26 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php @@ -0,0 +1,169 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') + ->desc('Create relationship attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-relationship-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel() + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('relatedCollectionId', '', new UID(), 'Related Collection ID.') + ->param('type', '', new WhiteList([ + Database::RELATION_ONE_TO_ONE, + Database::RELATION_MANY_TO_ONE, + Database::RELATION_MANY_TO_MANY, + Database::RELATION_ONE_TO_MANY + ], true), 'Relation type') + ->param('twoWay', false, new Boolean(), 'Is Two Way?', true) + ->param('key', null, new Key(), 'Attribute Key.', true) + ->param('twoWayKey', null, new Key(), 'Two Way Attribute Key.', true) + ->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([ + Database::RELATION_MUTATE_CASCADE, + Database::RELATION_MUTATE_RESTRICT, + Database::RELATION_MUTATE_SET_NULL + ], true), 'Constraints option', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $relatedCollectionId, + string $type, + bool $twoWay, + ?string $key, + ?string $twoWayKey, + string $onDelete, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $key ??= $relatedCollectionId; + $twoWayKey ??= $collectionId; + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId); + $relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId()); + if ($relatedCollection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') !== Database::VAR_RELATIONSHIP) { + continue; + } + + if (\strtolower($attribute->getId()) === \strtolower($key)) { + throw new Exception($this->getDuplicateException()); + } + + if ( + \strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && + $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId() + ) { + throw new Exception($this->getDuplicateException(), 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); + } + + if ( + $type === Database::RELATION_MANY_TO_MANY && + $attribute->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && + $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId() + ) { + $parentType = $this->isCollectionsAPI() ? 'collection' : 'table'; + throw new Exception($this->getDuplicateException(), "Creating more than one \"manyToMany\" relationship on the same $parentType is currently not permitted."); + } + } + + $attribute = $this->createAttribute($databaseId, $collectionId, new Document([ + 'key' => $key, + 'type' => Database::VAR_RELATIONSHIP, + 'size' => 0, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + 'options' => [ + 'relatedCollection' => $relatedCollectionId, + 'relationType' => $type, + 'twoWay' => $twoWay, + 'twoWayKey' => $twoWayKey, + 'onDelete' => $onDelete, + ] + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + foreach ($attribute->getAttribute('options', []) as $k => $option) { + $attribute->setAttribute($k, $option); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php new file mode 100644 index 0000000000..6416e7793e --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php @@ -0,0 +1,103 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') + ->desc('Update relationship attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-relationship-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel() + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('onDelete', null, new WhiteList([ + Database::RELATION_MUTATE_CASCADE, + Database::RELATION_MUTATE_RESTRICT, + Database::RELATION_MUTATE_SET_NULL + ], true), 'Constraints option', true) + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?string $onDelete, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_RELATIONSHIP, + required: false, + options: [ + 'onDelete' => $onDelete + ], + newKey: $newKey + ); + + foreach ($attribute->getAttribute('options', []) as $k => $option) { + $attribute->setAttribute($k, $option); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php new file mode 100644 index 0000000000..29e3ce757d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php @@ -0,0 +1,122 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_STRING); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/string') + ->desc('Create string attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-string-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel() + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?int $size, + ?bool $required, + ?string $default, + bool $array, + bool $encrypt, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + // Ensure default fits in the given size + $validator = new Text($size, 0); + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($this->getInvalidValueException(), $validator->getDescription()); + } + + $filters = []; + if ($encrypt) { + $filters[] = 'encrypt'; + } + + $attribute = $this->createAttribute( + $databaseId, + $collectionId, + new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => $size, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'filters' => $filters, + ]), + $response, + $dbForProject, + $queueForDatabase, + $queueForEvents + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php new file mode 100644 index 0000000000..b76a34951e --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php @@ -0,0 +1,102 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_STRING); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') + ->desc('Update string attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-string-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') + ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string attribute.', true) + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + ?int $size, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + databaseId: $databaseId, + collectionId: $collectionId, + key: $key, + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + type: Database::VAR_STRING, + size: $size, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php new file mode 100644 index 0000000000..5b1906ac2b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php @@ -0,0 +1,96 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_URL); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/url') + ->desc('Create URL attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') + ->label('audits.event', 'attribute.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-url-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + bool $array, + UtopiaResponse $response, + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents + ): void { + $attribute = $this->createAttribute($databaseId, $collectionId, new Document([ + 'key' => $key, + 'type' => Database::VAR_STRING, + 'size' => 2000, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_URL, + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php new file mode 100644 index 0000000000..253649f15a --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php @@ -0,0 +1,98 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_URL); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') + ->desc('Update URL attribute') + ->groups(['api', 'database', 'schema']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->label('audits.event', 'attribute.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-url-attribute.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('key', '', new Key(), 'Attribute Key.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') + ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action( + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + ?string $newKey, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents + ): void { + $attribute = $this->updateAttribute( + $databaseId, + $collectionId, + $key, + $dbForProject, + $queueForEvents, + type: Database::VAR_STRING, + filter: APP_DATABASE_ATTRIBUTE_URL, + default: $default, + required: $required, + newKey: $newKey + ); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($attribute, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php new file mode 100644 index 0000000000..072f6987d2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php @@ -0,0 +1,130 @@ +setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_LIST); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes') + ->desc('List attributes') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/list-attributes.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel() + ) + ] + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($table->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $queries = Query::parseQueries($queries); + + \array_push( + $queries, + Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]) + ); + + $cursor = \array_filter( + $queries, + fn ($query) => \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]) + ); + $cursor = \reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $attributeId = $cursor->getValue(); + $cursorDocument = Authorization::skip( + fn () => $dbForProject->find('attributes', [ + Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('key', [$attributeId]), + Query::limit(1), + ]) + ); + + if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { + $type = ucfirst($this->getContext()); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "$type '{$attributeId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument[0]); + } + + $filters = Query::groupByType($queries)['filters']; + + try { + $attributes = $dbForProject->find('attributes', $queries); + $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); + } catch (OrderException $e) { + $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; + $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; + $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } + + $response->dynamic(new Document([ + 'total' => $total, + $this->getSdkGroup() => $attributes, + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php index d00bc1f9f9..2ebcdb4c3f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php @@ -13,7 +13,6 @@ use Utopia\Platform\Action as UtopiaAction; */ abstract class Action extends UtopiaAction { - /** * The current API context (either 'table' or 'collection'). */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php index 775d33ab19..29ce39789e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php @@ -58,7 +58,7 @@ class Create extends ColumnAction ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) + ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the column in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php index 35fe6bc86d..ca692d9269 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php @@ -61,7 +61,7 @@ class Update extends ColumnAction ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string attribute.', true) + ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string column.', true) ->param('newKey', null, new Key(), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php index 8a6c009e87..860c32ee4b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Create.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\Create as CollectionCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php index e06c19b308..1c70401a40 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Delete.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\Delete as CollectionDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php index e5f2954de5..f7f850a0fe 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\Get as CollectionGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php index 67ff31efb7..9b86dcff8f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables\Logs; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\Logs\XList as CollectionLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php index f27232353e..4a20770cbd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Update.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\Update as CollectionUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php index 647a98d952..0371eaa3f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Usage/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables\Usage; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\Usage\Get as CollectionUsageGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php index 77eb556301..4381597ec6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Tables; -use Appwrite\Platform\Modules\Databases\Http\Collections\Action; use Appwrite\Platform\Modules\Databases\Http\Collections\XList as CollectionXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; From b55521e3de5c7470cb3793d2b603110a2655ce2a Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 09:36:13 +0530 Subject: [PATCH 061/173] update: address minor comments by `coderabbitai`. --- .../Databases/Http/Attributes/Action.php | 4 ++-- .../Http/Attributes/Boolean/Create.php | 2 +- .../Http/Attributes/Email/Create.php | 22 +++++++++---------- .../Databases/Http/Collections/XList.php | 3 +-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php index 66e8313d33..6d729b8cf3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php @@ -451,11 +451,11 @@ abstract class Action extends UtopiaAction throw new Exception($this->getNotAvailableException()); } - if ($attribute->getAttribute(('type') !== $type)) { + if ($attribute->getAttribute('type') !== $type) { throw new Exception($this->getTypeInvalidException()); } - if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) { + if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute('filter') !== $filter) { throw new Exception($this->getTypeInvalidException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php index 583e42b4ed..1db55ba33d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php @@ -34,7 +34,7 @@ class Create extends Action ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') ->desc('Create boolean attribute') ->groups(['api', 'database', 'schema']) - ->label('event', 'databases.[databaseId].collections.[collectionId].collections.[attributeId].create') + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'attribute.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php index 9b63f2198a..c33ce9daeb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php @@ -55,7 +55,7 @@ class Create extends Action ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Collection ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) @@ -68,20 +68,20 @@ class Create extends Action } public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - bool $array, + string $databaseId, + string $collectionId, + string $key, + ?bool $required, + ?string $default, + bool $array, UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents + Database $dbForProject, + EventDatabase $queueForDatabase, + Event $queueForEvents ): void { $attribute = $this->createAttribute( $databaseId, - $tableId, + $collectionId, new Document([ 'key' => $key, 'type' => Database::VAR_STRING, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php index 98246defaa..b6d002a59b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/XList.php @@ -97,8 +97,7 @@ class XList extends Action $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionIdId); if ($cursorDocument->isEmpty()) { - $message = - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, ucfirst($this->getContext()) . " '$collectionIdId' for the 'cursor' value not found."); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, ucfirst($this->getContext()) . " '$collectionIdId' for the 'cursor' value not found."); } $cursor->setValue($cursorDocument); From fc456b4cca28da0d5b67a8c1f6439d9edef997e0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 10:40:35 +0530 Subject: [PATCH 062/173] update: abstraction over attribute and column apis. --- .../Http/Attributes/Boolean/Create.php | 3 +- .../Http/Attributes/Boolean/Update.php | 3 +- .../Modules/Databases/Http/Columns/Action.php | 417 ------------------ .../Databases/Http/Columns/Boolean/Create.php | 40 +- .../Databases/Http/Columns/Boolean/Update.php | 41 +- .../Http/Columns/Datetime/Create.php | 59 +-- .../Http/Columns/Datetime/Update.php | 49 +- .../Modules/Databases/Http/Columns/Delete.php | 120 +---- .../Databases/Http/Columns/Email/Create.php | 57 +-- .../Databases/Http/Columns/Email/Update.php | 50 +-- .../Databases/Http/Columns/Enum/Create.php | 64 +-- .../Databases/Http/Columns/Enum/Update.php | 52 +-- .../Databases/Http/Columns/Float/Create.php | 72 +-- .../Databases/Http/Columns/Float/Update.php | 59 +-- .../Modules/Databases/Http/Columns/Get.php | 87 +--- .../Databases/Http/Columns/IP/Create.php | 49 +- .../Databases/Http/Columns/IP/Update.php | 50 +-- .../Databases/Http/Columns/Integer/Create.php | 74 +--- .../Databases/Http/Columns/Integer/Update.php | 59 +-- .../Http/Columns/Relationship/Create.php | 110 +---- .../Http/Columns/Relationship/Update.php | 53 +-- .../Databases/Http/Columns/String/Create.php | 71 +-- .../Databases/Http/Columns/String/Update.php | 52 +-- .../Databases/Http/Columns/URL/Create.php | 49 +- .../Databases/Http/Columns/URL/Update.php | 49 +- .../Modules/Databases/Http/Columns/XList.php | 88 +--- 26 files changed, 294 insertions(+), 1583 deletions(-) delete mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php index 1db55ba33d..57cff8a75f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php @@ -30,7 +30,8 @@ class Create extends Action { $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); - $this->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') ->desc('Create boolean attribute') ->groups(['api', 'database', 'schema']) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php index 41a0c5a621..7a2db8e74f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php @@ -30,7 +30,8 @@ class Update extends Action { $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); - $this->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') ->desc('Update boolean attribute') ->groups(['api', 'database', 'schema']) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php deleted file mode 100644 index a9415fded3..0000000000 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Action.php +++ /dev/null @@ -1,417 +0,0 @@ -getAttribute('key'); - $type = $column->getAttribute('type', ''); - $size = $column->getAttribute('size', 0); - $required = $column->getAttribute('required', true); - $signed = $column->getAttribute('signed', true); // integers are signed by default - $array = $column->getAttribute('array', false); - $format = $column->getAttribute('format', ''); - $formatOptions = $column->getAttribute('formatOptions', []); - $filters = $column->getAttribute('filters', []); // filters are hidden from the endpoint - $default = $column->getAttribute('default'); - $options = $column->getAttribute('options', []); - - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - if (!empty($format)) { - if (!Structure::hasFormat($format, $type)) { - throw new Exception(Exception::COLUMN_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns."); - } - } - - // Must throw here since dbForProject->createAttribute is performed by db worker - if ($required && isset($default)) { - throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); - } - - if ($array && isset($default)) { - throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); - } - - if ($type === Database::VAR_RELATIONSHIP) { - $options['side'] = Database::RELATION_SIDE_PARENT; - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); - if ($relatedTable->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND, 'The related table was not found.'); - } - } - - try { - $column = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), - 'key' => $key, - 'databaseInternalId' => $db->getInternalId(), - 'databaseId' => $db->getId(), - 'collectionInternalId' => $table->getInternalId(), - 'collectionId' => $tableId, - 'type' => $type, - 'status' => 'processing', // processing, available, failed, deleting, stuck - 'size' => $size, - 'required' => $required, - 'signed' => $signed, - 'default' => $default, - 'array' => $array, - 'format' => $format, - 'formatOptions' => $formatOptions, - 'filters' => $filters, - 'options' => $options, - ]); - - $dbForProject->checkAttribute($table, $column); - $column = $dbForProject->createDocument('attributes', $column); - } catch (DuplicateException) { - throw new Exception(Exception::COLUMN_ALREADY_EXISTS); - } catch (LimitException) { - throw new Exception(Exception::COLUMN_LIMIT_EXCEEDED); - } catch (Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - throw $e; - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - - if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { - $twoWayKey = $options['twoWayKey']; - $options['relatedCollection'] = $table->getId(); - $options['twoWayKey'] = $key; - $options['side'] = Database::RELATION_SIDE_CHILD; - - try { - $twoWayAttribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey), - 'key' => $twoWayKey, - 'databaseInternalId' => $db->getInternalId(), - 'databaseId' => $db->getId(), - 'collectionInternalId' => $relatedTable->getInternalId(), - 'collectionId' => $relatedTable->getId(), - 'type' => $type, - 'status' => 'processing', // processing, available, failed, deleting, stuck - 'size' => $size, - 'required' => $required, - 'signed' => $signed, - 'default' => $default, - 'array' => $array, - 'format' => $format, - 'formatOptions' => $formatOptions, - 'filters' => $filters, - 'options' => $options, - ]); - - $dbForProject->checkAttribute($relatedTable, $twoWayAttribute); - $dbForProject->createDocument('attributes', $twoWayAttribute); - } catch (DuplicateException) { - $dbForProject->deleteDocument('attributes', $column->getId()); - throw new Exception(Exception::COLUMN_ALREADY_EXISTS); - } catch (LimitException) { - $dbForProject->deleteDocument('attributes', $column->getId()); - throw new Exception(Exception::COLUMN_LIMIT_EXCEEDED); - } catch (Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); - throw $e; - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); - } - - $queueForDatabase - ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) - ->setDatabase($db) - ->setTable($table) - ->setRow($column); - - $queueForEvents - ->setContext('table', $table) - ->setContext('database', $db) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('columnId', $column->getId()); - - $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); - - return $column; - } - - protected function updateColumn( - string $databaseId, - string $tableId, - string $key, - Database $dbForProject, - Event $queueForEvents, - string $type, - int $size = null, - string $filter = null, - string|bool|int|float $default = null, - bool $required = null, - int|float|null $min = null, - int|float|null $max = null, - array $elements = null, - array $options = [], - string $newKey = null, - ): Document { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - - if ($column->isEmpty()) { - throw new Exception(Exception::COLUMN_NOT_FOUND); - } - - if ($column->getAttribute('status') !== 'available') { - throw new Exception(Exception::COLUMN_NOT_AVAILABLE); - } - - if ($column->getAttribute(('type') !== $type)) { - throw new Exception(Exception::COLUMN_TYPE_INVALID); - } - - if ($column->getAttribute('type') === Database::VAR_STRING && $column->getAttribute(('filter') !== $filter)) { - throw new Exception(Exception::COLUMN_TYPE_INVALID); - } - - if ($required && isset($default)) { - throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column'); - } - - if ($column->getAttribute('array', false) && isset($default)) { - throw new Exception(Exception::COLUMN_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns'); - } - - $tableId = 'database_' . $db->getInternalId() . '_collection_' . $table->getInternalId(); - - $column - ->setAttribute('default', $default) - ->setAttribute('required', $required); - - if (!empty($size)) { - $column->setAttribute('size', $size); - } - - switch ($column->getAttribute('format')) { - case APP_DATABASE_ATTRIBUTE_INT_RANGE: - case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE: - $min ??= $column->getAttribute('formatOptions')['min']; - $max ??= $column->getAttribute('formatOptions')['max']; - - if ($min > $max) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); - } - - if ($column->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { - $validator = new Range($min, $max, Database::VAR_INTEGER); - } else { - $validator = new Range($min, $max, Database::VAR_FLOAT); - - if (!is_null($default)) { - $default = \floatval($default); - } - } - - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); - } - - $options = [ - 'min' => $min, - 'max' => $max - ]; - $column->setAttribute('formatOptions', $options); - - break; - case APP_DATABASE_ATTRIBUTE_ENUM: - if (empty($elements)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Enum elements must not be empty'); - } - - foreach ($elements as $element) { - if (\strlen($element) === 0) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Each enum element must not be empty'); - } - } - - if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Default value not found in elements'); - } - - $options = [ - 'elements' => $elements - ]; - - $column->setAttribute('formatOptions', $options); - - break; - } - - if ($type === Database::VAR_RELATIONSHIP) { - $primaryRowOptions = \array_merge($column->getAttribute('options', []), $options); - $column->setAttribute('options', $primaryRowOptions); - try { - $dbForProject->updateRelationship( - collection: $tableId, - id: $key, - newKey: $newKey, - onDelete: $primaryRowOptions['onDelete'], - ); - } catch (NotFoundException) { - throw new Exception(Exception::COLUMN_NOT_FOUND); - } - - if ($primaryRowOptions['twoWay']) { - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryRowOptions['relatedCollection']); - - $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey']); - - if (!empty($newKey) && $newKey !== $key) { - $options['twoWayKey'] = $newKey; - } - - $relatedOptions = \array_merge($relatedColumn->getAttribute('options'), $options); - $relatedColumn->setAttribute('options', $relatedOptions); - $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey'], $relatedColumn); - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - } - } else { - try { - $dbForProject->updateAttribute( - collection: $tableId, - id: $key, - size: $size, - required: $required, - default: $default, - formatOptions: $options, - newKey: $newKey ?? null - ); - } catch (TruncateException) { - throw new Exception(Exception::COLUMN_INVALID_RESIZE); - } catch (NotFoundException) { - throw new Exception(Exception::COLUMN_NOT_FOUND); - } catch (LimitException) { - throw new Exception(Exception::COLUMN_LIMIT_EXCEEDED); - } catch (IndexException $e) { - throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); - } - } - - if (!empty($newKey) && $key !== $newKey) { - $originalUid = $column->getId(); - - $column - ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $newKey)) - ->setAttribute('key', $newKey); - - $dbForProject->updateDocument('attributes', $originalUid, $column); - - /** - * @var Document $index - */ - foreach ($table->getAttribute('indexes') as $index) { - /** - * @var string[] $columns - */ - $columns = $index->getAttribute('attributes', []); - $found = \array_search($key, $columns); - - if ($found !== false) { - $columns[$found] = $newKey; - $index->setAttribute('attributes', $columns); - $dbForProject->updateDocument('indexes', $index->getId(), $index); - } - } - } else { - $column = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key, $column); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $table->getId()); - - $queueForEvents - ->setContext('table', $table) - ->setContext('database', $db) - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('columnId', $column->getId()); - - return $column; - } -} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php index 7e1331a637..385c631493 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php @@ -4,21 +4,19 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Boolean; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Boolean\Create as BooleanCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; -class Create extends ColumnAction +class Create extends BooleanCreate { use HTTP; @@ -29,9 +27,12 @@ class Create extends ColumnAction public function __construct() { - $this->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_BOOLEAN); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean') ->desc('Create boolean column') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') @@ -41,14 +42,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createBooleanColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-boolean-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_BOOLEAN, + model: $this->getResponseModel(), ) ] )) @@ -62,23 +63,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void - { - - $column = $this->createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_BOOLEAN, - 'size' => 0, - 'required' => $required, - 'default' => $default, - 'array' => $array, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_BOOLEAN); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php index 6725e0c817..7b0754ccc7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Boolean; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Boolean\Update as BooleanUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,13 +12,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; -class Update extends ColumnAction +class Update extends BooleanUpdate { use HTTP; @@ -29,9 +28,12 @@ class Update extends ColumnAction public function __construct() { - $this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_BOOLEAN); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean/:key') ->desc('Update boolean column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -41,14 +43,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateBooleanColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-boolean-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_BOOLEAN, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -62,25 +64,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void - { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_BOOLEAN, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_BOOLEAN); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php index 29ce39789e..80031d61ab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php @@ -4,22 +4,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Datetime; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Datetime\Create as DatetimeCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; -class Create extends ColumnAction +class Create extends DatetimeCreate { use HTTP; @@ -30,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_DATETIME); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime') ->desc('Create datetime column') ->groups(['api', 'database']) ->label('scope', 'collections.write') @@ -43,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createDatetimeColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-datetime-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_DATETIME, + model: $this->getResponseModel(), ) ] )) @@ -64,43 +64,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $filters = ['datetime']; - - $column = $this->createColumn( - $databaseId, - $tableId, - new Document([ - 'key' => $key, - 'type' => Database::VAR_DATETIME, - 'size' => 0, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'filters' => $filters, - ]), - $response, - $dbForProject, - $queueForDatabase, - $queueForEvents - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_DATETIME); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php index 6ff0744859..d7124981d1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Datetime; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Datetime\Update as DatetimeUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -13,13 +13,12 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; -class Update extends ColumnAction +class Update extends DatetimeUpdate { use HTTP; @@ -30,10 +29,12 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_DATETIME); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime/:key') ->desc('Update dateTime column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +44,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateDatetimeColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-datetime-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_DATETIME, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -64,34 +65,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_DATETIME, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_DATETIME); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php index 717e8f958c..eb2c6a9ccf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php @@ -4,22 +4,19 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Delete as AttributesDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class Delete extends Action +class Delete extends AttributesDelete { use HTTP; @@ -30,10 +27,14 @@ class Delete extends Action public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + + // parent action handles multiple model types internally + $this->setResponseModel(UtopiaResponse::MODEL_NONE); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') ->desc('Delete column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +44,14 @@ class Delete extends Action ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'deleteColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/delete-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_NOCONTENT, - model: UtopiaResponse::MODEL_NONE, + model: $this->getResponseModel(), ) ], contentType: ContentType::NONE @@ -62,103 +63,8 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents, - ): void { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($db->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - if ($column->isEmpty()) { - throw new Exception(Exception::COLUMN_NOT_FOUND); - } - - $validator = new IndexDependencyValidator( - $table->getAttribute('indexes'), - $dbForProject->getAdapter()->getSupportForCastIndexArray(), - ); - if (!$validator->isValid($column)) { - throw new Exception(Exception::INDEX_DEPENDENCY); - } - - if ($column->getAttribute('status') === 'available') { - $column = $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'deleting')); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId()); - - if ($column->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $column->getAttribute('options'); - if ($options['twoWay']) { - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - if ($relatedTable->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); - if ($relatedColumn->isEmpty()) { - throw new Exception(Exception::COLUMN_NOT_FOUND); - } - - if ($relatedColumn->getAttribute('status') === 'available') { - $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'deleting')); - } - - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); - } - } - - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setTable($table) - ->setDatabase($db) - ->setRow($column); - - $type = $column->getAttribute('type'); - $format = $column->getAttribute('format'); - - $model = match ($type) { - Database::VAR_BOOLEAN => UtopiaResponse::MODEL_COLUMN_BOOLEAN, - Database::VAR_INTEGER => UtopiaResponse::MODEL_COLUMN_INTEGER, - Database::VAR_FLOAT => UtopiaResponse::MODEL_COLUMN_FLOAT, - Database::VAR_DATETIME => UtopiaResponse::MODEL_COLUMN_DATETIME, - Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, - Database::VAR_STRING => match ($format) { - APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_COLUMN_EMAIL, - APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_COLUMN_ENUM, - APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_COLUMN_IP, - APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_COLUMN_URL, - default => UtopiaResponse::MODEL_COLUMN_STRING, - }, - default => UtopiaResponse::MODEL_COLUMN, - }; - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('columnId', $column->getId()) - ->setContext('table', $table) - ->setContext('database', $db) - ->setPayload($response->output($column, $model)); - - $response->noContent(); + ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php index 2372d8b032..2fdb994251 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php @@ -5,21 +5,19 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Email; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Network\Validator\Email; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Email\Create as EmailCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; -class Create extends ColumnAction +class Create extends EmailCreate { use HTTP; @@ -30,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_EMAIL); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/email') ->desc('Create email column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createEmailColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-email-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_EMAIL, + model: $this->getResponseModel(), ) ] )) @@ -64,41 +64,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - bool $array, - \Appwrite\Utopia\Response $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $column = $this->createColumn( - $databaseId, - $tableId, - new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => 254, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), - $response, - $dbForProject, - $queueForDatabase, - $queueForEvents - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_EMAIL); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php index 74124eba37..255e636a39 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php @@ -4,7 +4,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Email; use Appwrite\Event\Event; use Appwrite\Network\Validator\Email; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Email\Update as EmailUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -13,13 +13,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; -class Update extends ColumnAction +class Update extends EmailUpdate { use HTTP; @@ -30,10 +29,12 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_EMAIL); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/email/:key') ->desc('Update email column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +44,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateEmailColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-email-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_EMAIL, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -64,35 +65,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_EMAIL, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_EMAIL); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php index c57d451743..2408aeeae4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php @@ -4,24 +4,21 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Enum; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Enum\Create as EnumCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Text; -class Create extends ColumnAction +class Create extends EnumCreate { use HTTP; @@ -32,10 +29,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_ENUM); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/enum') ->desc('Create enum column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -45,14 +44,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createEnumColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-attribute-enum.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_ENUM, + model: $this->getResponseModel(), ) ] )) @@ -67,47 +66,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - array $elements, - ?bool $required, - ?string $default, - bool $array, - \Appwrite\Utopia\Response $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - if (!is_null($default) && !in_array($default, $elements, true)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Default value not found in elements'); - } - - $column = $this->createColumn( - $databaseId, - $tableId, - new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_ENUM, - 'formatOptions' => ['elements' => $elements], - ]), - $response, - $dbForProject, - $queueForDatabase, - $queueForEvents - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_ENUM); + ->callback(function (string $databaseId, string $tableId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $elements, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php index aa588a9f1b..c47766b4dd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Enum; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Enum\Update as EnumUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; @@ -20,7 +19,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\Text; -class Update extends ColumnAction +class Update extends EnumUpdate { use HTTP; @@ -31,10 +30,12 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_ENUM); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/enum/:key') ->desc('Update enum column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -44,14 +45,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateEnumColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-enum-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_ENUM, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -66,37 +67,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?array $elements, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_ENUM, - default: $default, - required: $required, - elements: $elements, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_ENUM); + ->callback(function (string $databaseId, string $tableId, string $key, array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $elements, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index a0a16fafec..c8b9a7ec7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -4,24 +4,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Float; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Float\Create as FloatCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; -use Utopia\Validator\Range; -class Create extends ColumnAction +class Create extends FloatCreate { use HTTP; @@ -32,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_FLOAT); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/float') ->desc('Create float column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -45,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createFloatColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-float-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_FLOAT, + model: $this->getResponseModel(), ) ] )) @@ -68,54 +66,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?float $min, - ?float $max, - ?float $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $min ??= -PHP_FLOAT_MAX; - $max ??= PHP_FLOAT_MAX; - - if ($min > $max) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); - } - - $validator = new Range($min, $max, Database::VAR_FLOAT); - if (!\is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); - } - - $column = $this->createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_FLOAT, - 'size' => 0, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, - 'formatOptions' => ['min' => $min, 'max' => $max], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $formatOptions = $column->getAttribute('formatOptions', []); - if (!empty($formatOptions)) { - $column->setAttribute('min', \floatval($formatOptions['min'])); - $column->setAttribute('max', \floatval($formatOptions['max'])); - } - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_FLOAT); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php index 8ea7ab474a..1e008de2c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Float; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Float\Update as FloatUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,14 +12,13 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; use Utopia\Validator\Nullable; -class Update extends ColumnAction +class Update extends FloatUpdate { use HTTP; @@ -30,10 +29,12 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_FLOAT); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/float/:key') ->desc('Update float column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +44,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateFloatColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-float-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_FLOAT, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -66,44 +67,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?float $min, - ?float $max, - ?float $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_FLOAT, - default: $default, - required: $required, - min: $min, - max: $max, - newKey: $newKey - ); - - $formatOptions = $column->getAttribute('formatOptions', []); - if (!empty($formatOptions)) { - $column->setAttribute('min', \floatval($formatOptions['min'])); - $column->setAttribute('max', \floatval($formatOptions['max'])); - } - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_FLOAT); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php index 4390f0841c..d8847f0122 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php @@ -2,20 +2,18 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Get as AttributesGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class Get extends Action +class Get extends AttributesGet { use HTTP; @@ -26,35 +24,38 @@ class Get extends Action public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + + $this->setResponseModel([ + UtopiaResponse::MODEL_COLUMN_BOOLEAN, + UtopiaResponse::MODEL_COLUMN_INTEGER, + UtopiaResponse::MODEL_COLUMN_FLOAT, + UtopiaResponse::MODEL_COLUMN_EMAIL, + UtopiaResponse::MODEL_COLUMN_ENUM, + UtopiaResponse::MODEL_COLUMN_URL, + UtopiaResponse::MODEL_COLUMN_IP, + UtopiaResponse::MODEL_COLUMN_DATETIME, + UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, + UtopiaResponse::MODEL_COLUMN_STRING, + ]); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key') ->desc('Get column') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'getColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/get-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: [ - UtopiaResponse::MODEL_COLUMN_BOOLEAN, - UtopiaResponse::MODEL_COLUMN_INTEGER, - UtopiaResponse::MODEL_COLUMN_FLOAT, - UtopiaResponse::MODEL_COLUMN_EMAIL, - UtopiaResponse::MODEL_COLUMN_ENUM, - UtopiaResponse::MODEL_COLUMN_URL, - UtopiaResponse::MODEL_COLUMN_IP, - UtopiaResponse::MODEL_COLUMN_DATETIME, - UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, - UtopiaResponse::MODEL_COLUMN_STRING, - ] + model: $this->getResponseModel() ) ] )) @@ -63,50 +64,8 @@ class Get extends Action ->param('key', '', new Key(), 'Column Key.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key); - if ($column->isEmpty()) { - throw new Exception(Exception::COLUMN_NOT_FOUND); - } - - $type = $column->getAttribute('type'); - $format = $column->getAttribute('format'); - $options = $column->getAttribute('options', []); - - foreach ($options as $optKey => $optValue) { - $column->setAttribute($optKey, $optValue); - } - - $model = match ($type) { - Database::VAR_BOOLEAN => UtopiaResponse::MODEL_COLUMN_BOOLEAN, - Database::VAR_INTEGER => UtopiaResponse::MODEL_COLUMN_INTEGER, - Database::VAR_FLOAT => UtopiaResponse::MODEL_COLUMN_FLOAT, - Database::VAR_DATETIME => UtopiaResponse::MODEL_COLUMN_DATETIME, - Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, - Database::VAR_STRING => match ($format) { - APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_COLUMN_EMAIL, - APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_COLUMN_ENUM, - APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_COLUMN_IP, - APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_COLUMN_URL, - default => UtopiaResponse::MODEL_COLUMN_STRING, - }, - default => UtopiaResponse::MODEL_COLUMN, - }; - - $response->dynamic($column, $model); + ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $tableId, $key, $response, $dbForProject); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php index 73d8c93031..a69cc6ccfe 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php @@ -4,22 +4,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\IP; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\IP\Create as IPCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; -class Create extends ColumnAction +class Create extends IPCreate { use HTTP; @@ -30,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_IP); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/ip') ->desc('Create IP address column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createIpColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-ip-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_IP, + model: $this->getResponseModel(), ) ] )) @@ -64,33 +64,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - bool $array, - \Appwrite\Utopia\Response $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $column = $this->createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => 39, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_IP); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php index 4b9fa8bca8..d4c6db12fe 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\IP; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\IP\Update as IPUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,14 +12,13 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; use Utopia\Validator\Nullable; -class Update extends ColumnAction +class Update extends IPUpdate { use HTTP; @@ -30,10 +29,12 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_IP); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/ip/:key') ->desc('Update IP address column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +44,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateIpColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-ip-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_IP, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -64,35 +65,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_IP, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_IP); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php index cf2244bf1f..2c394dd042 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php @@ -4,24 +4,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Integer; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Integer\Create as IntegerCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; -use Utopia\Validator\Range; -class Create extends ColumnAction +class Create extends IntegerCreate { use HTTP; @@ -32,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_INTEGER); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/integer') ->desc('Create integer column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -45,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createIntegerColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-integer-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_INTEGER, + model: $this->getResponseModel(), ) ] )) @@ -68,56 +66,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?int $min, - ?int $max, - ?int $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $min ??= \PHP_INT_MIN; - $max ??= \PHP_INT_MAX; - - if ($min > $max) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); - } - - $validator = new Range($min, $max, Database::VAR_INTEGER); - if (!\is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); - } - - $size = $max > 2147483647 ? 8 : 4; - - $column = $this->createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_INTEGER, - 'size' => $size, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE, - 'formatOptions' => ['min' => $min, 'max' => $max], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $formatOptions = $column->getAttribute('formatOptions', []); - if (!empty($formatOptions)) { - $column->setAttribute('min', \intval($formatOptions['min'])); - $column->setAttribute('max', \intval($formatOptions['max'])); - } - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_INTEGER); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php index 1806cc91c3..4eacb2b0df 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Integer; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Integer\Update as IntegerUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,14 +12,13 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; use Utopia\Validator\Nullable; -class Update extends ColumnAction +class Update extends IntegerUpdate { use HTTP; @@ -30,10 +29,12 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_INTEGER); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/integer/:key') ->desc('Update integer column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +44,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateIntegerColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-integer-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_INTEGER, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -66,44 +67,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?int $min, - ?int $max, - ?int $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_INTEGER, - default: $default, - required: $required, - min: $min, - max: $max, - newKey: $newKey - ); - - $formatOptions = $column->getAttribute('formatOptions', []); - if (!empty($formatOptions)) { - $column->setAttribute('min', \intval($formatOptions['min'])); - $column->setAttribute('max', \intval($formatOptions['max'])); - } - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_INTEGER); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index 8b3ec616a3..d0b080b237 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -4,24 +4,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Relationship; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Relationship\Create as RelationshipCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\WhiteList; -class Create extends ColumnAction +class Create extends RelationshipCreate { use HTTP; @@ -32,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/relationship') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/relationship') ->desc('Create relationship column') ->groups(['api', 'database']) ->label('scope', 'collections.write') @@ -45,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createRelationshipColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-relationship-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_RELATIONSHIP + model: $this->getResponseModel() ) ] )) @@ -77,92 +75,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $relatedTableId, - string $type, - bool $twoWay, - ?string $key, - ?string $twoWayKey, - string $onDelete, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $key ??= $relatedTableId; - $twoWayKey ??= $tableId; - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - $table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId()); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $relatedTableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId); - $relatedTable = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedTableDocument->getInternalId()); - if ($relatedTable->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $columns = $table->getAttribute('attributes', []); - foreach ($columns as $column) { - if ($column->getAttribute('type') !== Database::VAR_RELATIONSHIP) { - continue; - } - - if (\strtolower($column->getId()) === \strtolower($key)) { - throw new Exception(Exception::COLUMN_ALREADY_EXISTS); - } - - if ( - \strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) && - $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() - ) { - throw new Exception(Exception::COLUMN_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.'); - } - - if ( - $type === Database::RELATION_MANY_TO_MANY && - $column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY && - $column->getAttribute('options')['relatedCollection'] === $relatedTable->getId() - ) { - throw new Exception(Exception::COLUMN_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same table is currently not permitted.'); - } - } - - $column = $this->createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_RELATIONSHIP, - 'size' => 0, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - 'options' => [ - 'relatedCollection' => $relatedTableId, - 'relationType' => $type, - 'twoWay' => $twoWay, - 'twoWayKey' => $twoWayKey, - 'onDelete' => $onDelete, - ] - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - foreach ($column->getAttribute('options', []) as $k => $option) { - $column->setAttribute($k, $option); - } - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); + ->callback(function (string $databaseId, string $tableId, string $relatedTableId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $relatedTableId, $type, $twoWayKey, $key, $twoWayKey, $onDelete, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php index 80ca25e9cf..85e4e207a5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\Relationship; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\Relationship\Update as RelationshipUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,12 +12,11 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; -class Update extends ColumnAction +class Update extends RelationshipUpdate { use HTTP; @@ -28,8 +27,11 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key/relationship') ->desc('Update relationship column') @@ -41,14 +43,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateRelationshipColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-relationship-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_RELATIONSHIP + model: $this->getResponseModel() ) ], contentType: ContentType::JSON @@ -65,39 +67,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?string $onDelete, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - $databaseId, - $tableId, - $key, - $dbForProject, - $queueForEvents, - type: Database::VAR_RELATIONSHIP, - required: false, - options: [ - 'onDelete' => $onDelete - ], - newKey: $newKey - ); - - foreach ($column->getAttribute('options', []) as $k => $option) { - $column->setAttribute($k, $option); - } - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); + ->callback(function (string $databaseId, string $tableId, string $key, ?string $onDelete, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $onDelete, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php index e768fda1d6..e00bdf679e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php @@ -4,17 +4,14 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\String; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\String\Create as StringCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; @@ -22,7 +19,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\Range; use Utopia\Validator\Text; -class Create extends ColumnAction +class Create extends StringCreate { use HTTP; @@ -33,10 +30,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_STRING); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/string') ->desc('Create string column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -46,14 +45,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createStringColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-string-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_STRING + model: $this->getResponseModel() ) ] )) @@ -69,54 +68,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?int $size, - ?bool $required, - ?string $default, - bool $array, - bool $encrypt, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - // Ensure default fits in the given size - $validator = new Text($size, 0); - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception(Exception::COLUMN_VALUE_INVALID, $validator->getDescription()); - } - - $filters = []; - if ($encrypt) { - $filters[] = 'encrypt'; - } - - $column = $this->createColumn( - $databaseId, - $tableId, - new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => $size, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'filters' => $filters, - ]), - $response, - $dbForProject, - $queueForDatabase, - $queueForEvents - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_STRING); + ->callback(function (string $databaseId, string $tableId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $size, $required, $default, $array, $encrypt, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php index ca692d9269..9db47fba69 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\String; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\String\Update as StringUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; @@ -21,7 +20,7 @@ use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; -class Update extends ColumnAction +class Update extends StringUpdate { use HTTP; @@ -32,9 +31,12 @@ class Update extends ColumnAction public function __construct() { - $this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_STRING); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/string/:key') ->desc('Update string column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -44,14 +46,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateStringColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-string-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_STRING, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -66,36 +68,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - ?int $size, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - databaseId: $databaseId, - tableId: $tableId, - key: $key, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - type: Database::VAR_STRING, - size: $size, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_STRING); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $size, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php index c1483c373a..ee770e9e4d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php @@ -4,22 +4,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\URL; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\URL\Create as URLCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\URL; -class Create extends ColumnAction +class Create extends URLCreate { use HTTP; @@ -30,10 +28,12 @@ class Create extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_URL); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url') ->desc('Create URL column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -43,14 +43,14 @@ class Create extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'createUrlColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-url-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_COLUMN_URL, + model: $this->getResponseModel(), ) ] )) @@ -64,33 +64,8 @@ class Create extends ColumnAction ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - $column = $this->createColumn($databaseId, $tableId, new Document([ - 'key' => $key, - 'type' => Database::VAR_STRING, - 'size' => 2000, - 'required' => $required, - 'default' => $default, - 'array' => $array, - 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_URL); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php index db35cf56c4..360445750b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns\URL; use Appwrite\Event\Event; -use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction; +use Appwrite\Platform\Modules\Databases\Http\Attributes\URL\Update as URLUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -12,14 +12,13 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\URL; -class Update extends ColumnAction +class Update extends URLUpdate { use HTTP; @@ -30,8 +29,11 @@ class Update extends ColumnAction public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_URL); + $this - ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url/:key') ->desc('Update URL column') @@ -43,14 +45,14 @@ class Update extends ColumnAction ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'updateUrlColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-url-attribute.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_URL, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -64,35 +66,8 @@ class Update extends ColumnAction ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); - } - - public function action( - string $databaseId, - string $tableId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { - $column = $this->updateColumn( - $databaseId, - $tableId, - $key, - $dbForProject, - $queueForEvents, - type: Database::VAR_STRING, - filter: APP_DATABASE_ATTRIBUTE_URL, - default: $default, - required: $required, - newKey: $newKey - ); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($column, UtopiaResponse::MODEL_COLUMN_URL); + ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php index c480536e6c..06d7a57dcf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -2,24 +2,18 @@ namespace Appwrite\Platform\Modules\Databases\Http\Columns; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Attributes\XList as AttributesXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Columns; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\Order as OrderException; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class XList extends Action +class XList extends AttributesXList { use HTTP; @@ -30,24 +24,26 @@ class XList extends Action public function __construct() { + $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_LIST); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes') ->desc('List columns') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'listColumns', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/list-attributes.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_COLUMN_LIST + model: $this->getResponseModel() ) ] )) @@ -56,70 +52,8 @@ class XList extends Action ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Columns::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $queries = Query::parseQueries($queries); - - \array_push( - $queries, - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$table->getInternalId()]) - ); - - $cursor = \array_filter( - $queries, - fn ($query) => \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]) - ); - $cursor = \reset($cursor); - - if ($cursor) { - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $columnId = $cursor->getValue(); - $cursorDocument = Authorization::skip( - fn () => $dbForProject->find('attributes', [ - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$table->getInternalId()]), - Query::equal('key', [$columnId]), - Query::limit(1), - ]) - ); - - if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Column '{$columnId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument[0]); - } - - $filters = Query::groupByType($queries)['filters']; - - try { - $columns = $dbForProject->find('attributes', $queries); - $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); - } - - $response->dynamic(new Document([ - 'columns' => $columns, - 'total' => $total, - ]), UtopiaResponse::MODEL_COLUMN_LIST); + ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $tableId, $queries, $response, $dbForProject); + }); } } From cb64484c440a91ad6f8b50c3d96eeb568c5ba9c4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 11:59:55 +0530 Subject: [PATCH 063/173] update: abstraction over document and row apis. --- .../Databases/Http/Attributes/Action.php | 2 - .../Databases/Http/Documents/Action.php | 138 ++++++++ .../Databases/Http/Documents/Create.php | 318 ++++++++++++++++++ .../Databases/Http/Documents/Delete.php | 174 ++++++++++ .../Modules/Databases/Http/Documents/Get.php | 157 +++++++++ .../Databases/Http/Documents/Logs/XList.php | 164 +++++++++ .../Databases/Http/Documents/Update.php | 305 +++++++++++++++++ .../Databases/Http/Documents/XList.php | 231 +++++++++++++ .../Modules/Databases/Http/Rows/Create.php | 261 +------------- .../Modules/Databases/Http/Rows/Delete.php | 124 ++----- .../Modules/Databases/Http/Rows/Get.php | 114 +------ .../Databases/Http/Rows/Logs/XList.php | 110 +----- .../Modules/Databases/Http/Rows/Update.php | 252 +------------- .../Modules/Databases/Http/Rows/XList.php | 184 +--------- .../Databases/Http/Tables/Logs/XList.php | 5 - 15 files changed, 1575 insertions(+), 964 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/Action.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/Logs/XList.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Documents/XList.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php index 6d729b8cf3..3cad730b6d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php @@ -50,8 +50,6 @@ abstract class Action extends UtopiaAction /** * Get the current context. - * - * @throws \LogicException If context has not been set. */ final protected function getContext(): string { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Action.php new file mode 100644 index 0000000000..d12a9bb8a7 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Action.php @@ -0,0 +1,138 @@ +context = $context; + } + + /** + * Get the current context. + */ + final protected function getContext(): string + { + return $this->context; + } + + /** + * Returns true if current context is Collections API. + */ + final protected function isCollectionsAPI(): bool + { + // rows in tables api context + // documents in collections api context + return $this->getContext() === DATABASE_DOCUMENTS_CONTEXT; + } + + /** + * Get the SDK group name for the current action. + * + * Can be used for XList operations as well! + */ + final protected function getSdkGroup(): string + { + return $this->isCollectionsAPI() ? 'documents' : 'rows'; + } + + /** + * Get the correct parent param key (e.g. `tableId` or `collectionId`) + */ + final protected function getParentEventsParamKey(): string + { + return $this->isCollectionsAPI() ? 'collectionId' : 'tableId'; + } + + /** + * Get the correct param key (e.g. `documentId` or `rowId`) + */ + final protected function getEventsParamKey(): string + { + return $this->getContext() . 'Id'; + } + + /** + * Get the appropriate parent level not found exception. + */ + final protected function getParentNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_NOT_FOUND + : Exception::TABLE_NOT_FOUND; + } + + /** + * Get the appropriate not found exception. + */ + final protected function getNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_NOT_FOUND + : Exception::ROW_NOT_FOUND; + } + + /** + * Get the appropriate already exists exception. + */ + final protected function getDuplicateException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_ALREADY_EXISTS + : Exception::ROW_ALREADY_EXISTS; + } + + + /** + * Get the correct invalid structure message. + */ + final protected function getInvalidStructureException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_INVALID_STRUCTURE + : Exception::ROW_INVALID_STRUCTURE; + } + + /** + * Get the appropriate missing data exception. + */ + final protected function getMissingDataException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_MISSING_DATA + : Exception::ROW_MISSING_DATA; + } + + /** + * Get the appropriate missing payload exception. + */ + final protected function getMissingPayloadException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_MISSING_PAYLOAD + : Exception::ROW_MISSING_PAYLOAD; + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Create.php new file mode 100644 index 0000000000..a6e938bc18 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Create.php @@ -0,0 +1,318 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') + ->desc('Create document') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'row.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', [ + new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + ) + ]) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') + ->param('data', [], new JSON(), 'Document data as JSON object.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('response') + ->inject('dbForProject') + ->inject('user') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + + if (empty($data)) { + throw new Exception($this->getMissingDataException()); + } + + if (isset($data['$id'])) { + // `rows` or `documents` in message. + throw new Exception($this->getInvalidStructureException(), '$id is not allowed for creating new ' . $this->getContext() . 's, try update instead'); + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception($this->getParentNotFoundException()); + } + + $allowedPermissions = [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]; + + // Map aggregate permissions to into the set of individual permissions they represent. + $permissions = Permission::aggregate($permissions, $allowedPermissions); + + // Add permissions for current the user if none were provided. + if (\is_null($permissions)) { + $permissions = []; + if (!empty($user->getId())) { + foreach ($allowedPermissions as $permission) { + $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); + } + } + } + + // Users can only manage their own roles, API keys and Admin users can manage any + if (!$isAPIKey && !$isPrivilegedUser) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); + } + } + } + } + + $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers + $data['$id'] = $documentId == 'unique()' ? ID::unique() : $documentId; + $data['$permissions'] = $permissions; + $document = new Document($data); + + $operations = 0; + + $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) { + $operations++; + + $documentSecurity = $collection->getAttribute('documentSecurity', false); + $validator = new Authorization($permission); + + $valid = $validator->isValid($collection->getPermissionsByType($permission)); + if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + + if ($permission === Database::PERMISSION_UPDATE) { + $valid = $valid || $validator->isValid($document->getUpdate()); + if ($documentSecurity && !$valid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + } + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; + } + + $relatedTableId = $relationship->getAttribute('relatedCollection'); + $relatedTable = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + ); + + foreach ($relations as &$relation) { + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } + if ($relation instanceof Document) { + $current = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId()) + ); + + if ($current->isEmpty()) { + $type = Database::PERMISSION_CREATE; + + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { + $relation['$id'] = ID::unique(); + } + } else { + $relation->removeAttribute('$collectionId'); + $relation->removeAttribute('$databaseId'); + $relation->setAttribute('$collection', $relatedTable->getId()); + $type = Database::PERMISSION_UPDATE; + } + + $checkPermissions($relatedTable, $relation, $type); + } + } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } + } + }; + + $checkPermissions($collection, $document, Database::PERMISSION_CREATE); + + try { + $document = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (NotFoundException) { + throw new Exception($this->getParentNotFoundException()); + } + + + // Add $collectionId and $databaseId for all documents + $processDocument = function (Document $table, Document $document) use (&$processDocument, $dbForProject, $database) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processDocument($relatedCollection, $relation); + } + } + } + }; + + $processDocument($collection, $document); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection + + $response->addHeader('X-Debug-Operations', $operations); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($document, $this->getResponseModel()); + + $relationships = \array_map( + fn ($row) => $document->getAttribute('key'), + \array_filter( + $collection->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam($this->getEventsParamKey(), $document->getId()) + ->setPayload($response->getPayload(), sensitive: $relationships) + ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Delete.php new file mode 100644 index 0000000000..93a4fdb57e --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Delete.php @@ -0,0 +1,174 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') + ->desc('Delete document') + ->groups(['api', 'database']) + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete') + ->label('audits.event', 'document.delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/delete-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('documentId', '', new UID(), 'Document ID.') + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception($this->getParentNotFoundException()); + } + + // Read permission should not be required for delete + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + + if ($document->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + try { + $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) { + $dbForProject->deleteDocument( + 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + $documentId + ); + }); + } catch (NotFoundException) { + throw new Exception($this->getParentNotFoundException()); + } + + // Add $collection and $databaseId for all documents + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processDocument($relatedCollection, $relation); + } + } + } + }; + + $processDocument($collection, $document); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection + + $response->addHeader('X-Debug-Operations', 1); + + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setParam($this->getEventsParamKey(), $document->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection) + ->setPayload($response->output($document, $this->getResponseModel()), sensitive: $relationships); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Get.php new file mode 100644 index 0000000000..ddc08f4783 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Get.php @@ -0,0 +1,157 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') + ->desc('Get document') + ->groups(['api', 'database']) + ->label('scope', 'documents.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/get-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception($this->getParentNotFoundException()); + } + + try { + $queries = Query::parseQueries($queries); + $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + if ($document->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + $operations = 0; + + // Add $collectionId and $databaseId for all rows + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations) { + if ($document->isEmpty()) { + return; + } + + $operations++; + + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + if (\in_array(\gettype($related), ['array', 'object'])) { + $operations++; + } + + continue; + } + + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processDocument($relatedCollection, $relation); + } + } + } + }; + + $processDocument($collection, $document); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); + + $response->addHeader('X-Debug-Operations', $operations); + + $response->dynamic($document, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Logs/XList.php new file mode 100644 index 0000000000..991c43ca1c --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Logs/XList.php @@ -0,0 +1,164 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') + ->desc('List document logs') + ->groups(['api', 'database']) + ->label('scope', 'documents.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'logs', + name: self::getName(), + description: '/docs/references/databases/get-document-logs.md', + auth: [AuthType::ADMIN], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON, + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) + ->inject('response') + ->inject('dbForProject') + ->inject('locale') + ->inject('geodb') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId); + + if ($document->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + // Temp fix for logs + $queries[] = Query::or([ + Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), + Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), + ]); + + $audit = new Audit($dbForProject); + // getContext() => `document` or `row`. + $resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/' .$this->getContext(). '/' . $document->getId(); + $logs = $audit->getLogsByResource($resource, $queries); + + $output = []; + + foreach ($logs as $i => &$log) { + $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; + + $detector = new Detector($log['userAgent']); + $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) + + $os = $detector->getOS(); + $client = $detector->getClient(); + $device = $detector->getDevice(); + + $output[$i] = new Document([ + 'event' => $log['event'], + 'userId' => $log['data']['userId'], + 'userEmail' => $log['data']['userEmail'] ?? null, + 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'ip' => $log['ip'], + 'time' => $log['time'], + 'osCode' => $os['osCode'], + 'osName' => $os['osName'], + 'osVersion' => $os['osVersion'], + 'clientType' => $client['clientType'], + 'clientCode' => $client['clientCode'], + 'clientName' => $client['clientName'], + 'clientVersion' => $client['clientVersion'], + 'clientEngine' => $client['clientEngine'], + 'clientEngineVersion' => $client['clientEngineVersion'], + 'deviceName' => $device['deviceName'], + 'deviceBrand' => $device['deviceBrand'], + 'deviceModel' => $device['deviceModel'] + ]); + + $record = $geodb->get($log['ip']); + + if ($record) { + $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; + $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); + } else { + $output[$i]['countryCode'] = '--'; + $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); + } + } + + $response->dynamic(new Document([ + 'logs' => $output, + 'total' => $audit->countLogsByResource($resource, $queries), + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Update.php new file mode 100644 index 0000000000..e55b9bda39 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/Update.php @@ -0,0 +1,305 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->desc('Update document') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'document.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + + if (empty($data) && \is_null($permissions)) { + throw new Exception($this->getMissingPayloadException()); + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception($this->getParentNotFoundException()); + } + + // Read permission should not be required for update + /** @var Document $document */ + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + + if ($document->isEmpty()) { + throw new Exception($this->getNotFoundException()); + } + + // Map aggregate permissions into the multiple permissions they represent. + $permissions = Permission::aggregate($permissions, [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]); + + // Users can only manage their own roles, API keys and Admin users can manage any + $roles = Authorization::getRoles(); + if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + } + } + } + } + + if (\is_null($permissions)) { + $permissions = $document->getPermissions() ?? []; + } + + $data['$id'] = $documentId; + $data['$permissions'] = $permissions; + $newDocument = new Document($data); + + $operations = 0; + + $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, &$operations) { + + $operations++; + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + ); + + foreach ($relations as &$relation) { + // If the relation is an array it can be either update or create a child document. + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } + if ($relation instanceof Document) { + $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( + 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + $relation->getId() + )); + $relation->removeAttribute('$collectionId'); + $relation->removeAttribute('$databaseId'); + // Attribute $collection is required for Utopia. + $relation->setAttribute( + '$collection', + 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId() + ); + + if ($oldDocument->isEmpty()) { + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { + $relation['$id'] = ID::unique(); + } + } + $setCollection($relatedCollection, $relation); + } + } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } + } + }); + + $setCollection($collection, $newDocument); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); + + $response->addHeader('X-Debug-Operations', $operations); + + try { + $document = $dbForProject->withRequestTimestamp( + $requestTimestamp, + fn () => $dbForProject->updateDocument( + 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + $document->getId(), + $newDocument + ) + ); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + } catch (NotFoundException) { + throw new Exception($this->getParentNotFoundException()); + } + + // Add $collectionId and $databaseId for all documents + $processDocument = function (Document $table, Document $row) use (&$processDocument, $dbForProject, $database) { + $row->setAttribute('$databaseId', $database->getId()); + $row->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $row->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processDocument($relatedCollection, $relation); + } + } + } + }; + + $processDocument($collection, $document); + + $response->dynamic($document, $this->getResponseModel()); + + $relationships = \array_map( + fn ($row) => $document->getAttribute('key'), + \array_filter( + $collection->getAttribute('attributes', []), + fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam($this->getEventsParamKey(), $document->getId()) + ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection) + ->setPayload($response->getPayload(), sensitive: $relationships); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Documents/XList.php new file mode 100644 index 0000000000..48779ae71d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Documents/XList.php @@ -0,0 +1,231 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') + ->desc('List documents') + ->groups(['api', 'database']) + ->label('scope', 'documents.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/list-documents.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->callback([$this, 'action']); + } + + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception($this->getParentNotFoundException()); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + + $cursor = \reset($cursor); + + if ($cursor) { + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $documentId = $cursor->getValue(); + + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + + if ($cursorDocument->isEmpty()) { + $type = ucfirst($this->getContext()); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "$type '{$documentId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + try { + $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); + } catch (OrderException $e) { + $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; + $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; + $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } + + $operations = 0; + + // Add $collectionId and $databaseId for all documents + $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations): bool { + if ($document->isEmpty()) { + return false; + } + + $operations++; + + $document->removeAttribute('$collection'); + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + if (\in_array(\gettype($related), ['array', 'object'])) { + $operations++; + } + + continue; + } + + if (!\is_array($related)) { + $relations = [$related]; + } else { + $relations = $related; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + // todo: Use local cache for this getDocument + $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + + foreach ($relations as $index => $doc) { + if ($doc instanceof Document) { + if (!$processDocument($relatedCollection, $doc)) { + unset($relations[$index]); + } + } + } + + if (\is_array($related)) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } elseif (empty($relations)) { + $document->setAttribute($relationship->getAttribute('key'), null); + } + } + + return true; + }); + + foreach ($documents as $row) { + $processDocument($collection, $row); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); + + $response->addHeader('X-Debug-Operations', $operations); + + $select = \array_reduce($queries, function ($result, $query) { + return $result || ($query->getMethod() === Query::TYPE_SELECT); + }, false); + + // Check if the SELECT query includes $databaseId and $collectionId + $hasDatabaseId = false; + $hasCollectionId = false; + if ($select) { + $hasDatabaseId = \array_reduce($queries, function ($result, $query) { + return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues())); + }, false); + $hasCollectionId = \array_reduce($queries, function ($result, $query) { + return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues())); + }, false); + } + + if ($select) { + foreach ($documents as $row) { + if (!$hasDatabaseId) { + $row->removeAttribute('$databaseId'); + } + if (!$hasCollectionId) { + $row->removeAttribute('$collectionId'); + } + } + } + + $response->dynamic(new Document([ + 'total' => $total, + // rows or documents + $this->getSdkGroup() => $documents, + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php index 87078a5d3d..fa0fca4101 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Create.php @@ -2,10 +2,9 @@ namespace Appwrite\Platform\Modules\Databases\Http\Rows; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Documents\Create as DocumentCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -14,21 +13,13 @@ use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Exception\NotFound as NotFoundException; -use Utopia\Database\Exception\Structure as StructureException; -use Utopia\Database\Helpers\ID; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Helpers\Role; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; -class Create extends Action +class Create extends DocumentCreate { use HTTP; @@ -37,12 +28,18 @@ class Create extends Action return 'createRow'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_ROW; + } + public function __construct() { + $this->setContext(DATABASE_ROWS_CONTEXT); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents') ->desc('Create row') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') @@ -56,14 +53,14 @@ class Create extends Action ->label('sdk', [ new Method( namespace: 'databases', - group: 'rows', - name: 'createRow', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, - model: UtopiaResponse::MODEL_ROW, + model: self::getResponseModel(), ) ], contentType: ContentType::JSON @@ -79,236 +76,8 @@ class Create extends Action ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage): void - { - $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array - - if (empty($data)) { - throw new Exception(Exception::ROW_MISSING_DATA); - } - - if (isset($data['$id'])) { - throw new Exception(Exception::ROW_INVALID_STRUCTURE, '$id is not allowed for creating new rows, try update instead'); - } - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $allowedPermissions = [ - Database::PERMISSION_READ, - Database::PERMISSION_UPDATE, - Database::PERMISSION_DELETE, - ]; - - // Map aggregate permissions to into the set of individual permissions they represent. - $permissions = Permission::aggregate($permissions, $allowedPermissions); - - // Add permissions for current the user if none were provided. - if (\is_null($permissions)) { - $permissions = []; - if (!empty($user->getId())) { - foreach ($allowedPermissions as $permission) { - $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); - } - } - } - - // Users can only manage their own roles, API keys and Admin users can manage any - if (!$isAPIKey && !$isPrivilegedUser) { - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); - } - } - } - } - - $data['$collection'] = $table->getId(); // Adding this param to make API easier for developers - $data['$id'] = $rowId == 'unique()' ? ID::unique() : $rowId; - $data['$permissions'] = $permissions; - $row = new Document($data); - - $operations = 0; - - $checkPermissions = function (Document $table, Document $row, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) { - $operations++; - - $documentSecurity = $table->getAttribute('documentSecurity', false); - $validator = new Authorization($permission); - - $valid = $validator->isValid($table->getPermissionsByType($permission)); - if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - - if ($permission === Database::PERMISSION_UPDATE) { - $valid = $valid || $validator->isValid($row->getUpdate()); - if ($documentSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - } - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - - $isList = \is_array($related) && \array_values($related) === $related; - - if ($isList) { - $relations = $related; - } else { - $relations = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($relations as &$relation) { - if ( - \is_array($relation) - && \array_values($relation) !== $relation - && !isset($relation['$id']) - ) { - $relation['$id'] = ID::unique(); - $relation = new Document($relation); - } - if ($relation instanceof Document) { - $current = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId()) - ); - - if ($current->isEmpty()) { - $type = Database::PERMISSION_CREATE; - - if (isset($relation['$id']) && $relation['$id'] === 'unique()') { - $relation['$id'] = ID::unique(); - } - } else { - $relation->removeAttribute('$collectionId'); - $relation->removeAttribute('$databaseId'); - $relation->setAttribute('$collection', $relatedTable->getId()); - $type = Database::PERMISSION_UPDATE; - } - - $checkPermissions($relatedTable, $relation, $type); - } - } - - if ($isList) { - $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } else { - $row->setAttribute($relationship->getAttribute('key'), \reset($relations)); - } - } - }; - - $checkPermissions($table, $row, Database::PERMISSION_CREATE); - - try { - $row = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $row); - } catch (StructureException $e) { - throw new Exception(Exception::ROW_INVALID_STRUCTURE, $e->getMessage()); - } catch (DuplicateException) { - throw new Exception(Exception::ROW_ALREADY_EXISTS); - } catch (NotFoundException) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection - - $response->addHeader('X-Debug-Operations', $operations); - - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($row, UtopiaResponse::MODEL_ROW); - - $relationships = \array_map( - fn ($row) => $row->getAttribute('key'), - \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('rowId', $row->getId()) - ->setContext('table', $table) - ->setContext('database', $database) - ->setPayload($response->getPayload(), sensitive: $relationships); + ->callback(function (string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + parent::action($databaseId, $rowId, $tableId, $databaseId, $permissions, $response, $dbForProject, $user, $queueForEvents, $queueForStatsUsage); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php index 61cd4d218a..476e4b83f2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Delete.php @@ -2,25 +2,20 @@ namespace Appwrite\Platform\Modules\Databases\Http\Rows; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Documents\Delete as DocumentDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\NotFound as NotFoundException; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class Delete extends Action +class Delete extends DocumentDelete { use HTTP; @@ -29,12 +24,24 @@ class Delete extends Action return 'deleteRow'; } + /** + * Same explanation as the parent action. + * + * 1. `SDKResponse` uses `UtopiaResponse::MODEL_NONE`. + * 2. But we later need the actual return type for events queue below! + */ + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_ROW; + } + public function __construct() { + $this->setContext(DATABASE_ROWS_CONTEXT); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') ->desc('Delete row') ->groups(['api', 'database']) ->label('scope', 'documents.write') @@ -47,8 +54,8 @@ class Delete extends Action ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( namespace: 'databases', - group: 'rows', - name: 'deleteRow', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/delete-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ @@ -67,101 +74,8 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - // Read permission should not be required for delete - $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - - if ($row->isEmpty()) { - throw new Exception(Exception::ROW_NOT_FOUND); - } - - try { - $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $table, $rowId) { - $dbForProject->deleteDocument( - 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), - $rowId - ); + ->callback(function (string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + parent::action($databaseId, $tableId, $rowId, $requestTimestamp, $response, $dbForProject, $queueForEvents, $queueForStatsUsage); }); - } catch (NotFoundException) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection - - $response->addHeader('X-Debug-Operations', 1); - - $relationships = \array_map( - fn ($row) => $row->getAttribute('key'), - \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('rowId', $row->getId()) - ->setContext('table', $table) - ->setContext('database', $database) - ->setPayload($response->output($row, UtopiaResponse::MODEL_ROW), sensitive: $relationships); - - $response->noContent(); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php index fb600f5a7f..8eb07ef94e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -2,28 +2,21 @@ namespace Appwrite\Platform\Modules\Databases\Http\Rows; -use Appwrite\Auth\Auth; use Appwrite\Event\StatsUsage; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Documents\Get as DocumentGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization as AuthorizationException; -use Utopia\Database\Exception\Query as QueryException; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; -class Get extends Action +class Get extends DocumentGet { use HTTP; @@ -32,8 +25,15 @@ class Get extends Action return 'getRow'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_ROW; + } + public function __construct() { + $this->setContext(DATABASE_ROWS_CONTEXT); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') @@ -44,14 +44,14 @@ class Get extends Action ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'rows', - name: 'getRow', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/get-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ROW, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -63,92 +63,8 @@ class Get extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId, $queries); - } catch (AuthorizationException) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - if ($row->isEmpty()) { - throw new Exception(Exception::ROW_NOT_FOUND); - } - - $operations = 0; - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations) { - if ($row->isEmpty()) { - return; - } - - $operations++; - - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { - $operations++; - } - - continue; - } - - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); - - $response->addHeader('X-Debug-Operations', $operations); - - $response->dynamic($row, UtopiaResponse::MODEL_ROW); + ->callback(function (string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { + parent::action($databaseId, $tableId, $rowId, $queries, $response, $dbForProject, $queueForStatsUsage); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php index 238815d849..ebf5bcca1b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Logs/XList.php @@ -2,31 +2,23 @@ namespace Appwrite\Platform\Modules\Databases\Http\Rows\Logs; -use Appwrite\Detector\Detector; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Documents\Logs\XList as DocumentLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use MaxMind\Db\Reader; -use Utopia\Audit\Audit; use Utopia\Database\Database; -use Utopia\Database\DateTime; -use Utopia\Database\Document; -use Utopia\Database\Exception\Query as QueryException; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; -class XList extends Action +class XList extends DocumentLogXList { use HTTP; @@ -37,10 +29,11 @@ class XList extends Action public function __construct() { + $this->setContext(DATABASE_ROWS_CONTEXT); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId/logs') ->desc('List row logs') ->groups(['api', 'database']) ->label('scope', 'documents.read') @@ -48,13 +41,13 @@ class XList extends Action ->label('sdk', new Method( namespace: 'databases', group: 'logs', - name: 'listRowLogs', + name: self::getName(), description: '/docs/references/databases/get-document-logs.md', auth: [AuthType::ADMIN], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_LOG_LIST, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON, @@ -67,93 +60,8 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - $row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId); - - if ($row->isEmpty()) { - throw new Exception(Exception::ROW_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - // Temp fix for logs - $queries[] = Query::or([ - Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), - Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), - ]); - - $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/table/' . $tableId . '/row/' . $row->getId(); - $logs = $audit->getLogsByResource($resource, $queries); - - $output = []; - - foreach ($logs as $i => &$log) { - $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - - $detector = new Detector($log['userAgent']); - $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - - $os = $detector->getOS(); - $client = $detector->getClient(); - $device = $detector->getDevice(); - - $output[$i] = new Document([ - 'event' => $log['event'], - 'userId' => $log['data']['userId'], - 'userEmail' => $log['data']['userEmail'] ?? null, - 'userName' => $log['data']['userName'] ?? null, - 'mode' => $log['data']['mode'] ?? null, - 'ip' => $log['ip'], - 'time' => $log['time'], - 'osCode' => $os['osCode'], - 'osName' => $os['osName'], - 'osVersion' => $os['osVersion'], - 'clientType' => $client['clientType'], - 'clientCode' => $client['clientCode'], - 'clientName' => $client['clientName'], - 'clientVersion' => $client['clientVersion'], - 'clientEngine' => $client['clientEngine'], - 'clientEngineVersion' => $client['clientEngineVersion'], - 'deviceName' => $device['deviceName'], - 'deviceBrand' => $device['deviceBrand'], - 'deviceModel' => $device['deviceModel'] - ]); - - $record = $geodb->get($log['ip']); - - if ($record) { - $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; - $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); - } else { - $output[$i]['countryCode'] = '--'; - $output[$i]['countryName'] = $locale->getText('locale.country.unknown'); - } - } - - $response->dynamic(new Document([ - 'total' => $audit->countLogsByResource($resource, $queries), - 'logs' => $output, - ]), UtopiaResponse::MODEL_LOG_LIST); + ->callback(function (string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb) { + parent::action($databaseId, $tableId, $rowId, $queries, $response, $dbForProject, $locale, $geodb); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index 5590aa6c67..d29a8dcd12 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -2,33 +2,22 @@ namespace Appwrite\Platform\Modules\Databases\Http\Rows; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Documents\Update as DocumentUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization as AuthorizationException; -use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Exception\NotFound as NotFoundException; -use Utopia\Database\Exception\Structure as StructureException; -use Utopia\Database\Helpers\ID; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Helpers\Role; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; -class Update extends Action +class Update extends DocumentUpdate { use HTTP; @@ -37,8 +26,15 @@ class Update extends Action return 'updateRow'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_ROW; + } + public function __construct() { + $this->setContext(DATABASE_ROWS_CONTEXT); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') @@ -55,14 +51,14 @@ class Update extends Action ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( namespace: 'databases', - group: 'rows', - name: 'updateRow', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/update-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ROW, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -77,226 +73,8 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void - { - - $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array - - if (empty($data) && \is_null($permissions)) { - throw new Exception(Exception::ROW_MISSING_PAYLOAD); - } - - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - // Read permission should not be required for update - /** @var Document $row */ - $row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - - if ($row->isEmpty()) { - throw new Exception(Exception::ROW_NOT_FOUND); - } - - // Map aggregate permissions into the multiple permissions they represent. - $permissions = Permission::aggregate($permissions, [ - Database::PERMISSION_READ, - Database::PERMISSION_UPDATE, - Database::PERMISSION_DELETE, - ]); - - // Users can only manage their own roles, API keys and Admin users can manage any - $roles = Authorization::getRoles(); - if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); - } - } - } - } - - if (\is_null($permissions)) { - $permissions = $row->getPermissions() ?? []; - } - - $data['$id'] = $rowId; - $data['$permissions'] = $permissions; - $newRow = new Document($data); - - $operations = 0; - - $setTable = (function (Document $table, Document $row) use (&$setTable, $dbForProject, $database, &$operations) { - - $operations++; - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - - $isList = \is_array($related) && \array_values($related) === $related; - - if ($isList) { - $relations = $related; - } else { - $relations = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($relations as &$relation) { - // If the relation is an array it can be either update or create a child document. - if ( - \is_array($relation) - && \array_values($relation) !== $relation - && !isset($relation['$id']) - ) { - $relation['$id'] = ID::unique(); - $relation = new Document($relation); - } - if ($relation instanceof Document) { - $oldRow = Authorization::skip(fn () => $dbForProject->getDocument( - 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), - $relation->getId() - )); - $relation->removeAttribute('$collectionId'); - $relation->removeAttribute('$databaseId'); - // Attribute $collection is required for Utopia. - $relation->setAttribute( - '$collection', - 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId() - ); - - if ($oldRow->isEmpty()) { - if (isset($relation['$id']) && $relation['$id'] === 'unique()') { - $relation['$id'] = ID::unique(); - } - } - $setTable($relatedTable, $relation); - } - } - - if ($isList) { - $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } else { - $row->setAttribute($relationship->getAttribute('key'), \reset($relations)); - } - } - }); - - $setTable($table, $newRow); - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); - - $response->addHeader('X-Debug-Operations', $operations); - - try { - $row = $dbForProject->withRequestTimestamp( - $requestTimestamp, - fn () => $dbForProject->updateDocument( - 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), - $row->getId(), - $newRow - ) - ); - } catch (AuthorizationException) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } catch (DuplicateException) { - throw new Exception(Exception::ROW_ALREADY_EXISTS); - } catch (StructureException $e) { - throw new Exception(Exception::ROW_INVALID_STRUCTURE, $e->getMessage()); - } catch (NotFoundException) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - // Add $tableId and $databaseId for all rows - $processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processRow($relatedTable, $relation); - } - } - } - }; - - $processRow($table, $row); - - $response->dynamic($row, UtopiaResponse::MODEL_ROW); - - $relationships = \array_map( - fn ($row) => $row->getAttribute('key'), - \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); - - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('rowId', $row->getId()) - ->setContext('table', $table) - ->setContext('database', $database) - ->setPayload($response->getPayload(), sensitive: $relationships); + ->callback(function (string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + parent::action($databaseId, $tableId, $rowId, $data, $permissions, $requestTimestamp, $response, $dbForProject, $queueForEvents, $queueForStatsUsage); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php index 781c0d7c83..a954312493 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -2,29 +2,21 @@ namespace Appwrite\Platform\Modules\Databases\Http\Rows; -use Appwrite\Auth\Auth; use Appwrite\Event\StatsUsage; -use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Http\Documents\XList as DocumentXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Exception\Order as OrderException; -use Utopia\Database\Exception\Query as QueryException; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; -class XList extends Action +class XList extends DocumentXList { use HTTP; @@ -33,8 +25,15 @@ class XList extends Action return 'listRows'; } + protected function getResponseModel(): string + { + return UtopiaResponse::MODEL_ROW_LIST; + } + public function __construct() { + $this->setContext(DATABASE_ROWS_CONTEXT); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') @@ -45,14 +44,14 @@ class XList extends Action ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'rows', - name: 'listRows', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/list-documents.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_ROW_LIST, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON @@ -63,161 +62,8 @@ class XList extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); - } - - public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void - { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId)); - - if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { - throw new Exception(Exception::TABLE_NOT_FOUND); - } - - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - - $cursor = \reset($cursor); - - if ($cursor) { - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $rowId = $cursor->getValue(); - - $cursorRow = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId)); - - if ($cursorRow->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Row '{$rowId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorRow); - } - try { - $rows = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries, APP_LIMIT_COUNT); - } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); - } - - $operations = 0; - - // Add $tableId and $databaseId for all rows - $processRow = (function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations): bool { - if ($row->isEmpty()) { - return false; - } - - $operations++; - - $row->removeAttribute('$collection'); - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { - $operations++; - } - - continue; - } - - if (!\is_array($related)) { - $relations = [$related]; - } else { - $relations = $related; - } - - $relatedTableId = $relationship->getAttribute('relatedCollection'); - // todo: Use local cache for this getDocument - $relatedTable = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)); - - foreach ($relations as $index => $doc) { - if ($doc instanceof Document) { - if (!$processRow($relatedTable, $doc)) { - unset($relations[$index]); - } - } - } - - if (\is_array($related)) { - $row->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } elseif (empty($relations)) { - $row->setAttribute($relationship->getAttribute('key'), null); - } - } - - return true; - }); - - foreach ($rows as $row) { - $processRow($table, $row); - } - - $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); - - $response->addHeader('X-Debug-Operations', $operations); - - $select = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT); - }, false); - - // Check if the SELECT query includes $databaseId and $collectionId - $hasDatabaseId = false; - $hasTableId = false; - if ($select) { - $hasDatabaseId = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues())); - }, false); - $hasTableId = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues())); - }, false); - } - - if ($select) { - foreach ($rows as $row) { - if (!$hasDatabaseId) { - $row->removeAttribute('$databaseId'); - } - if (!$hasTableId) { - $row->removeAttribute('$collectionId'); - } - } - } - - $response->dynamic(new Document([ - 'total' => $total, - 'rows' => $rows, - ]), UtopiaResponse::MODEL_ROW_LIST); + ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { + parent::action($databaseId, $tableId, $queries, $response, $dbForProject, $queueForStatsUsage); + }); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php index 9b86dcff8f..90983dc84b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Tables/Logs/XList.php @@ -27,11 +27,6 @@ class XList extends CollectionLogXList return 'listTableLogs'; } - protected function getResponseModel(): string - { - return UtopiaResponse::MODEL_LOG_LIST; - } - public function __construct() { $this->setContext(DATABASE_TABLES_CONTEXT); From 96b89693fc2a569f72ffd2b9adaddca2f3f35480 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 12:47:21 +0530 Subject: [PATCH 064/173] update: use `abstract` for `getResponseModel`. --- .../Databases/Http/Attributes/Action.php | 24 ++----------------- .../Http/Attributes/Boolean/Create.php | 22 ++++++----------- .../Http/Attributes/Boolean/Update.php | 20 ++++++---------- .../Http/Attributes/Datetime/Create.php | 21 ++++++---------- .../Http/Attributes/Datetime/Update.php | 20 ++++++---------- .../Databases/Http/Attributes/Delete.php | 22 +++++++---------- .../Http/Attributes/Email/Create.php | 21 ++++++---------- .../Http/Attributes/Email/Update.php | 20 ++++++---------- .../Databases/Http/Attributes/Enum/Create.php | 22 ++++++----------- .../Databases/Http/Attributes/Enum/Update.php | 21 ++++++---------- .../Http/Attributes/Float/Create.php | 23 ++++++------------ .../Http/Attributes/Float/Update.php | 22 ++++++----------- .../Modules/Databases/Http/Attributes/Get.php | 22 ++++++++--------- .../Databases/Http/Attributes/IP/Create.php | 21 ++++++---------- .../Databases/Http/Attributes/IP/Update.php | 20 ++++++---------- .../Http/Attributes/Integer/Create.php | 23 ++++++------------ .../Http/Attributes/Integer/Update.php | 22 ++++++----------- .../Http/Attributes/Relationship/Create.php | 23 ++++++------------ .../Http/Attributes/Relationship/Update.php | 7 ++++-- .../Http/Attributes/String/Create.php | 7 ++++-- .../Http/Attributes/String/Update.php | 7 ++++-- .../Databases/Http/Attributes/URL/Create.php | 7 ++++-- .../Databases/Http/Attributes/URL/Update.php | 7 ++++-- .../Databases/Http/Attributes/XList.php | 17 +++++++------ .../Databases/Http/Collections/Action.php | 10 ++++---- .../Databases/Http/Columns/Boolean/Create.php | 6 ++++- .../Databases/Http/Columns/Boolean/Update.php | 6 ++++- .../Http/Columns/Datetime/Create.php | 6 ++++- .../Http/Columns/Datetime/Update.php | 6 ++++- .../Modules/Databases/Http/Columns/Delete.php | 9 ++++--- .../Databases/Http/Columns/Email/Create.php | 6 ++++- .../Databases/Http/Columns/Email/Update.php | 6 ++++- .../Databases/Http/Columns/Enum/Create.php | 6 ++++- .../Databases/Http/Columns/Enum/Update.php | 6 ++++- .../Databases/Http/Columns/Float/Create.php | 6 ++++- .../Databases/Http/Columns/Float/Update.php | 6 ++++- .../Modules/Databases/Http/Columns/Get.php | 13 ++++++---- .../Databases/Http/Columns/IP/Create.php | 6 ++++- .../Databases/Http/Columns/IP/Update.php | 6 ++++- .../Databases/Http/Columns/Integer/Create.php | 6 ++++- .../Databases/Http/Columns/Integer/Update.php | 6 ++++- .../Http/Columns/Relationship/Create.php | 6 ++++- .../Http/Columns/Relationship/Update.php | 7 ++++-- .../Databases/Http/Columns/String/Create.php | 6 ++++- .../Databases/Http/Columns/String/Update.php | 6 ++++- .../Databases/Http/Columns/URL/Create.php | 6 ++++- .../Databases/Http/Columns/URL/Update.php | 7 ++++-- .../Modules/Databases/Http/Columns/XList.php | 6 ++++- .../Modules/Databases/Http/Indexes/Create.php | 2 +- .../Modules/Databases/Http/Indexes/Delete.php | 2 +- .../Modules/Databases/Http/Indexes/Get.php | 1 - .../Modules/Databases/Http/Indexes/XList.php | 1 - .../Modules/Databases/Http/Rows/Get.php | 1 - .../Modules/Databases/Http/Rows/Update.php | 1 - .../Modules/Databases/Http/Rows/XList.php | 1 - 55 files changed, 287 insertions(+), 323 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php index 3cad730b6d..0ac013fc4f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Action.php @@ -30,9 +30,9 @@ abstract class Action extends UtopiaAction private ?string $context = DATABASE_ATTRIBUTES_CONTEXT; /** - * @var string|array|null The current response model for the attribute/column type. + * Get the correct response model. */ - private string|array|null $responseModel = null; + abstract protected function getResponseModel(): string|array; /** * Set the context to either `column` or `attribute`. @@ -92,26 +92,6 @@ abstract class Action extends UtopiaAction return $this->getContext() . 'Id'; } - /** - * Set the correct response model. - */ - final protected function setResponseModel(string|array $model): void - { - $this->responseModel = $model; - } - - /** - * Get the correct response model. - */ - final protected function getResponseModel(): string|array - { - if ($this->responseModel === null) { - throw new \LogicException("Missing response model: you must call setResponseModel() before using it."); - } - - return $this->responseModel; - } - /** * Get the appropriate parent level not found exception. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php index 57cff8a75f..8fdbbefbcd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Create.php @@ -26,10 +26,13 @@ class Create extends Action return 'createBooleanColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') @@ -66,19 +69,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?bool $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { - + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $attribute = $this->createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, 'type' => Database::VAR_BOOLEAN, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php index 7a2db8e74f..b2fdc527b3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Boolean/Update.php @@ -26,10 +26,13 @@ class Update extends Action return 'updateBooleanColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') @@ -66,17 +69,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?bool $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php index 40d7aed1e9..07f7158475 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Create.php @@ -27,10 +27,13 @@ class Create extends Action return 'createDatetimeAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_DATETIME; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') @@ -67,18 +70,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?string $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $attribute = $this->createAttribute( $databaseId, $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php index 386d6682ad..0709415d81 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Datetime/Update.php @@ -27,10 +27,13 @@ class Update extends Action return 'updateDatetimeAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_DATETIME; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_DATETIME); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') @@ -67,17 +70,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php index da29fd09ee..bcb4d1d406 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Delete.php @@ -27,11 +27,14 @@ class Delete extends Action return 'deleteAttribute'; } - public function __construct() + protected function getResponseModel(): string|array { // we should correctly & carefully set the context later. - $this->setResponseModel(UtopiaResponse::MODEL_NONE); + return UtopiaResponse::MODEL_NONE; + } + public function __construct() + { $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') @@ -44,8 +47,8 @@ class Delete extends Action ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( namespace: 'databases', - group: 'attributes', - name: 'deleteAttribute', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/delete-attribute.md', auth: [AuthType::KEY], responses: [ @@ -66,15 +69,8 @@ class Delete extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php index c33ce9daeb..bd8512a271 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Create.php @@ -27,10 +27,13 @@ class Create extends Action return 'createEmailAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_EMAIL; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/email') @@ -67,18 +70,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?string $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $attribute = $this->createAttribute( $databaseId, $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php index 0c41961099..2c39e37b19 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Email/Update.php @@ -27,10 +27,13 @@ class Update extends Action return 'updateEmailAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_EMAIL; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_EMAIL); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') @@ -67,17 +70,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php index 61298cbe94..fa30639bd1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Create.php @@ -29,10 +29,13 @@ class Create extends Action return 'createEnumAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_ENUM; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_ENUM); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') @@ -70,19 +73,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - array $elements, - ?bool $required, - ?string $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { if (!is_null($default) && !\in_array($default, $elements, true)) { throw new Exception($this->getInvalidValueException(), 'Default value not found in elements'); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php index 37d8065608..089407ad01 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Enum/Update.php @@ -28,10 +28,13 @@ class Update extends Action return 'updateEnumAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_ENUM; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_ENUM); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') @@ -69,18 +72,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?array $elements, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php index 9603cf4c9a..e1f22ffdd8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Create.php @@ -29,10 +29,13 @@ class Create extends Action return 'createFloatAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_FLOAT; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/float') @@ -71,20 +74,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?float $min, - ?float $max, - ?float $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $min ??= -PHP_FLOAT_MAX; $max ??= PHP_FLOAT_MAX; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php index 012e8239c9..42cc5444d5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Float/Update.php @@ -27,10 +27,13 @@ class Update extends Action return 'updateFloatAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_FLOAT; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_FLOAT); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') @@ -69,19 +72,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?float $min, - ?float $max, - ?float $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php index e782897be2..95612b45ca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Get.php @@ -23,9 +23,9 @@ class Get extends Action return 'getColumn'; } - public function __construct() + protected function getResponseModel(): string|array { - $this->setResponseModel([ + return [ UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT, @@ -36,8 +36,11 @@ class Get extends Action UtopiaResponse::MODEL_ATTRIBUTE_DATETIME, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP, UtopiaResponse::MODEL_ATTRIBUTE_STRING, - ]); + ]; + } + public function __construct() + { $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') @@ -47,8 +50,8 @@ class Get extends Action ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'columns', - name: 'getColumn', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/get-attribute.md', auth: [AuthType::KEY], responses: [ @@ -66,13 +69,8 @@ class Get extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $tableId, - string $key, - UtopiaResponse $response, - Database $dbForProject - ): void { + public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void + { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php index 8176b03bba..927d55c9c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Create.php @@ -27,10 +27,13 @@ class Create extends Action return 'createIpAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_IP; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_IP); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') @@ -67,18 +70,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?string $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $attribute = $this->createAttribute( $databaseId, $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php index 868b735bbb..a489675bd2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/IP/Update.php @@ -27,10 +27,13 @@ class Update extends Action return 'updateIpAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_IP; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_IP); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') @@ -67,17 +70,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?string $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php index 99371b26d9..9dd013362c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Create.php @@ -29,10 +29,13 @@ class Create extends Action return 'createIntegerAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_INTEGER; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') @@ -71,20 +74,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?int $min, - ?int $max, - ?int $default, - bool $array, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $min ??= \PHP_INT_MIN; $max ??= \PHP_INT_MAX; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php index 51c72af363..6e1379daff 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Integer/Update.php @@ -27,10 +27,13 @@ class Update extends Action return 'updateIntegerAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_INTEGER; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_INTEGER); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') @@ -69,19 +72,8 @@ class Update extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $key, - ?bool $required, - ?int $min, - ?int $max, - ?int $default, - ?string $newKey, - UtopiaResponse $response, - Database $dbForProject, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void + { $attribute = $this->updateAttribute( databaseId: $databaseId, collectionId: $collectionId, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php index cdc1861c26..49d853bed6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Create.php @@ -29,10 +29,13 @@ class Create extends Action return 'createRelationshipAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') @@ -80,20 +83,8 @@ class Create extends Action ->callback([$this, 'action']); } - public function action( - string $databaseId, - string $collectionId, - string $relatedCollectionId, - string $type, - bool $twoWay, - ?string $key, - ?string $twoWayKey, - string $onDelete, - UtopiaResponse $response, - Database $dbForProject, - EventDatabase $queueForDatabase, - Event $queueForEvents - ): void { + public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + { $key ??= $relatedCollectionId; $twoWayKey ??= $collectionId; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php index 6416e7793e..9c95bd2a65 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/Relationship/Update.php @@ -25,10 +25,13 @@ class Update extends Action return 'updateRelationshipAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php index 29e3ce757d..47386f7530 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Create.php @@ -30,10 +30,13 @@ class Create extends Action return 'createStringAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_STRING; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_STRING); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/string') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php index b76a34951e..08c55e6021 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/String/Update.php @@ -29,10 +29,13 @@ class Update extends Action return 'updateStringAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_STRING; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_STRING); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php index 5b1906ac2b..38f5a4773a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Create.php @@ -27,10 +27,13 @@ class Create extends Action return 'createUrlAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_URL; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_URL); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/url') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php index 253649f15a..45309326aa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/URL/Update.php @@ -27,10 +27,13 @@ class Update extends Action return 'updateUrlAttribute'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_URL; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_URL); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php index 072f6987d2..5a190bd671 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Attributes/XList.php @@ -28,10 +28,13 @@ class XList extends Action return 'listAttributes'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_ATTRIBUTE_LIST; + } + public function __construct() { - $this->setResponseModel(UtopiaResponse::MODEL_ATTRIBUTE_LIST); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes') @@ -67,8 +70,8 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); - if ($table->isEmpty()) { + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } @@ -77,7 +80,7 @@ class XList extends Action \array_push( $queries, Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$table->getInternalId()]) + Query::equal('collectionInternalId', [$collection->getInternalId()]) ); $cursor = \array_filter( @@ -96,7 +99,7 @@ class XList extends Action $cursorDocument = Authorization::skip( fn () => $dbForProject->find('attributes', [ Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('key', [$attributeId]), Query::limit(1), ]) @@ -104,7 +107,7 @@ class XList extends Action if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { $type = ucfirst($this->getContext()); - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "$type '{$attributeId}' for the 'cursor' value not found."); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "$type '$attributeId' for the 'cursor' value not found."); } $cursor->setValue($cursorDocument[0]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php index 2ebcdb4c3f..83500fa964 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php @@ -18,6 +18,11 @@ abstract class Action extends UtopiaAction */ private ?string $context = DATABASE_COLLECTIONS_CONTEXT; + /** + * Get the response model used in the SDK and HTTP responses. + */ + abstract protected function getResponseModel(): string; + /** * Set the current API context. * @@ -50,11 +55,6 @@ abstract class Action extends UtopiaAction return $this->getContext() . 'Id'; } - /** - * Get the response model used in the SDK and HTTP responses. - */ - abstract protected function getResponseModel(): string; - /** * Determine if the current action is for the Collections API. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php index 385c631493..fafed3bf8d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Create.php @@ -25,10 +25,14 @@ class Create extends BooleanCreate return 'createBooleanColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_BOOLEAN; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_BOOLEAN); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php index 7b0754ccc7..7bb9e640ad 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Boolean/Update.php @@ -26,10 +26,14 @@ class Update extends BooleanUpdate return 'updateBooleanColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_BOOLEAN; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_BOOLEAN); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php index 80031d61ab..7d9dc8dcce 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Create.php @@ -26,10 +26,14 @@ class Create extends DatetimeCreate return 'createDatetimeColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_DATETIME; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_DATETIME); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php index d7124981d1..7e1b1e196d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Datetime/Update.php @@ -27,10 +27,14 @@ class Update extends DatetimeUpdate return 'updateDatetimeColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_DATETIME; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_DATETIME); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php index eb2c6a9ccf..e712f7cdde 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Delete.php @@ -25,13 +25,16 @@ class Delete extends AttributesDelete return 'deleteColumn'; } + // parent handles multiple model types internally + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_NONE; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - // parent action handles multiple model types internally - $this->setResponseModel(UtopiaResponse::MODEL_NONE); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php index 2fdb994251..a3dfc0d6a2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Create.php @@ -26,10 +26,14 @@ class Create extends EmailCreate return 'createEmailColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_EMAIL; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_EMAIL); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php index 255e636a39..2f8494250e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Email/Update.php @@ -27,10 +27,14 @@ class Update extends EmailUpdate return 'updateEmailColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_EMAIL; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_EMAIL); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php index 2408aeeae4..06fc55891e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Create.php @@ -27,10 +27,14 @@ class Create extends EnumCreate return 'createEnumColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_ENUM; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_ENUM); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php index c47766b4dd..28a4538b66 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Enum/Update.php @@ -28,10 +28,14 @@ class Update extends EnumUpdate return 'updateEnumColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_ENUM; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_ENUM); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php index c8b9a7ec7b..4f5db64d57 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Create.php @@ -26,10 +26,14 @@ class Create extends FloatCreate return 'createFloatColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_FLOAT; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_FLOAT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php index 1e008de2c0..c7ab1795db 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Float/Update.php @@ -27,10 +27,14 @@ class Update extends FloatUpdate return 'updateFloatColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_FLOAT; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_FLOAT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php index d8847f0122..ff69757b6d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Get.php @@ -22,11 +22,9 @@ class Get extends AttributesGet return 'getColumn'; } - public function __construct() + protected function getResponseModel(): string|array { - $this->setContext(DATABASE_COLUMNS_CONTEXT); - - $this->setResponseModel([ + return [ UtopiaResponse::MODEL_COLUMN_BOOLEAN, UtopiaResponse::MODEL_COLUMN_INTEGER, UtopiaResponse::MODEL_COLUMN_FLOAT, @@ -37,7 +35,12 @@ class Get extends AttributesGet UtopiaResponse::MODEL_COLUMN_DATETIME, UtopiaResponse::MODEL_COLUMN_RELATIONSHIP, UtopiaResponse::MODEL_COLUMN_STRING, - ]); + ]; + } + + public function __construct() + { + $this->setContext(DATABASE_COLUMNS_CONTEXT); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php index a69cc6ccfe..b42f9aaf6b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Create.php @@ -26,10 +26,14 @@ class Create extends IPCreate return 'createIpColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_IP; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_IP); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php index d4c6db12fe..101041a790 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/IP/Update.php @@ -27,10 +27,14 @@ class Update extends IPUpdate return 'updateIpColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_IP; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_IP); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php index 2c394dd042..b0b5eb59bb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Create.php @@ -26,10 +26,14 @@ class Create extends IntegerCreate return 'createIntegerColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_INTEGER; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_INTEGER); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php index 4eacb2b0df..faa42aa3ca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Integer/Update.php @@ -27,10 +27,14 @@ class Update extends IntegerUpdate return 'updateIntegerColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_INTEGER; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_INTEGER); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php index d0b080b237..2807041377 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Create.php @@ -26,10 +26,14 @@ class Create extends RelationshipCreate return 'createRelationshipColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_RELATIONSHIP; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php index 85e4e207a5..81040584b3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/Relationship/Update.php @@ -25,15 +25,18 @@ class Update extends RelationshipUpdate return 'updateRelationshipColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_RELATIONSHIP; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_RELATIONSHIP); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key/relationship') ->desc('Update relationship column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php index e00bdf679e..a75563a271 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Create.php @@ -28,10 +28,14 @@ class Create extends StringCreate return 'createStringColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_STRING; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_STRING); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php index 9db47fba69..0b1f370696 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/String/Update.php @@ -29,10 +29,14 @@ class Update extends StringUpdate return 'updateStringColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_STRING; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_STRING); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php index ee770e9e4d..5c575acb56 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Create.php @@ -26,10 +26,14 @@ class Create extends URLCreate return 'createUrlColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_URL; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_URL); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php index 360445750b..c37a74198a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/URL/Update.php @@ -27,15 +27,18 @@ class Update extends URLUpdate return 'updateUrlColumn'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_URL; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_URL); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url/:key') ->desc('Update URL column') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php index 06d7a57dcf..280e56fa48 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Columns/XList.php @@ -22,10 +22,14 @@ class XList extends AttributesXList return 'listColumns'; } + protected function getResponseModel(): string|array + { + return UtopiaResponse::MODEL_COLUMN_LIST; + } + public function __construct() { $this->setContext(DATABASE_COLUMNS_CONTEXT); - $this->setResponseModel(UtopiaResponse::MODEL_COLUMN_LIST); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php index 537d8e27f0..e9dd4c7092 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Create.php @@ -39,13 +39,13 @@ class Create extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes') ->desc('Create index') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'index.create') + // TODO: audits table or collections, check the context type if possible, move into another module. ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php index 78e1799164..dd7c4bd5e7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Delete.php @@ -32,13 +32,13 @@ class Delete extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') ->desc('Delete index') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') ->label('audits.event', 'index.delete') + // TODO: audits table or collections, check the context type if possible ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php index b1689481ed..a89819d844 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/Get.php @@ -30,7 +30,6 @@ class Get extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes/:key') ->desc('Get index') ->groups(['api', 'database']) ->label('scope', 'collections.read') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php index 53c623f9f4..b11806cfd0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Indexes/XList.php @@ -34,7 +34,6 @@ class XList extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/indexes') ->desc('List indexes') ->groups(['api', 'database']) ->label('scope', 'collections.read') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php index 8eb07ef94e..3ec0ea6213 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Get.php @@ -37,7 +37,6 @@ class Get extends DocumentGet $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') ->desc('Get row') ->groups(['api', 'database']) ->label('scope', 'documents.read') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php index d29a8dcd12..34d947796e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/Update.php @@ -38,7 +38,6 @@ class Update extends DocumentUpdate $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId') ->desc('Update row') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php index a954312493..041f532840 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Rows/XList.php @@ -37,7 +37,6 @@ class XList extends DocumentXList $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') - ->httpAlias('/v1/databases/:databaseId/collections/:tableId/documents') ->desc('List rows') ->groups(['api', 'database']) ->label('scope', 'documents.read') From da70da985ba1e61ca0eda5c0f29b74b8c82c590d Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 12:55:54 +0530 Subject: [PATCH 065/173] update: use nested directory structure. --- .../Databases/Http/{ => Databases}/Collections/Action.php | 2 +- .../Http/{ => Databases/Collections}/Attributes/Action.php | 2 +- .../{ => Databases/Collections}/Attributes/Boolean/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Boolean/Update.php | 4 ++-- .../Collections}/Attributes/Datetime/Create.php | 4 ++-- .../Collections}/Attributes/Datetime/Update.php | 4 ++-- .../Http/{ => Databases/Collections}/Attributes/Delete.php | 2 +- .../{ => Databases/Collections}/Attributes/Email/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Email/Update.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Enum/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Enum/Update.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Float/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Float/Update.php | 4 ++-- .../Http/{ => Databases/Collections}/Attributes/Get.php | 2 +- .../Http/{ => Databases/Collections}/Attributes/IP/Create.php | 4 ++-- .../Http/{ => Databases/Collections}/Attributes/IP/Update.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Integer/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/Integer/Update.php | 4 ++-- .../Collections}/Attributes/Relationship/Create.php | 4 ++-- .../Collections}/Attributes/Relationship/Update.php | 4 ++-- .../{ => Databases/Collections}/Attributes/String/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/String/Update.php | 4 ++-- .../{ => Databases/Collections}/Attributes/URL/Create.php | 4 ++-- .../{ => Databases/Collections}/Attributes/URL/Update.php | 4 ++-- .../Http/{ => Databases/Collections}/Attributes/XList.php | 2 +- .../Databases/Http/{ => Databases}/Collections/Create.php | 2 +- .../Databases/Http/{ => Databases}/Collections/Delete.php | 2 +- .../Http/{ => Databases/Collections}/Documents/Action.php | 2 +- .../Http/{ => Databases/Collections}/Documents/Create.php | 2 +- .../Http/{ => Databases/Collections}/Documents/Delete.php | 2 +- .../Http/{ => Databases/Collections}/Documents/Get.php | 2 +- .../Http/{ => Databases/Collections}/Documents/Logs/XList.php | 4 ++-- .../Http/{ => Databases/Collections}/Documents/Update.php | 2 +- .../Http/{ => Databases/Collections}/Documents/XList.php | 2 +- .../Databases/Http/{ => Databases}/Collections/Get.php | 2 +- .../Http/{ => Databases/Collections}/Indexes/Create.php | 0 .../Http/{ => Databases/Collections}/Indexes/Delete.php | 0 .../Http/{ => Databases/Collections}/Indexes/Get.php | 0 .../Http/{ => Databases/Collections}/Indexes/XList.php | 0 .../Databases/Http/{ => Databases}/Collections/Logs/XList.php | 4 ++-- .../Databases/Http/{ => Databases}/Collections/Update.php | 2 +- .../Databases/Http/{ => Databases}/Collections/Usage/Get.php | 4 ++-- .../Databases/Http/{ => Databases}/Collections/XList.php | 2 +- .../Http/{ => Databases/Tables}/Columns/Boolean/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Boolean/Update.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Datetime/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Datetime/Update.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Columns/Delete.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Email/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Email/Update.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Enum/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Enum/Update.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Float/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Float/Update.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Columns/Get.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/IP/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/IP/Update.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Integer/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/Integer/Update.php | 4 ++-- .../{ => Databases/Tables}/Columns/Relationship/Create.php | 4 ++-- .../{ => Databases/Tables}/Columns/Relationship/Update.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/String/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/String/Update.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/URL/Create.php | 4 ++-- .../Http/{ => Databases/Tables}/Columns/URL/Update.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Columns/XList.php | 4 ++-- .../Modules/Databases/Http/{ => Databases}/Tables/Create.php | 4 ++-- .../Modules/Databases/Http/{ => Databases}/Tables/Delete.php | 4 ++-- .../Modules/Databases/Http/{ => Databases}/Tables/Get.php | 4 ++-- .../Databases/Http/{ => Databases}/Tables/Logs/XList.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Rows/Create.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Rows/Delete.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Rows/Get.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Rows/Logs/XList.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Rows/Update.php | 4 ++-- .../Databases/Http/{ => Databases/Tables}/Rows/XList.php | 4 ++-- .../Modules/Databases/Http/{ => Databases}/Tables/Update.php | 4 ++-- .../Databases/Http/{ => Databases}/Tables/Usage/Get.php | 4 ++-- .../Modules/Databases/Http/{ => Databases}/Tables/XList.php | 4 ++-- 79 files changed, 134 insertions(+), 134 deletions(-) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Action.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Action.php (99%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Boolean/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Boolean/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Datetime/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Datetime/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Delete.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Email/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Email/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Enum/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Enum/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Float/Create.php (96%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Float/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Get.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/IP/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/IP/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Integer/Create.php (96%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Integer/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Relationship/Create.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/Relationship/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/String/Create.php (96%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/String/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/URL/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/URL/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Attributes/XList.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Create.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Delete.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/Action.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/Create.php (99%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/Delete.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/Get.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/Logs/XList.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/Update.php (99%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Documents/XList.php (99%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Get.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Indexes/Create.php (100%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Indexes/Delete.php (100%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Indexes/Get.php (100%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Collections}/Indexes/XList.php (100%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Logs/XList.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Update.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/Usage/Get.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Collections/XList.php (98%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Boolean/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Boolean/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Datetime/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Datetime/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Delete.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Email/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Email/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Enum/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Enum/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Float/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Float/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Get.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/IP/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/IP/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Integer/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Integer/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Relationship/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/Relationship/Update.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/String/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/String/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/URL/Create.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/URL/Update.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Columns/XList.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/Delete.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/Get.php (92%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/Logs/XList.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Rows/Create.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Rows/Delete.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Rows/Get.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Rows/Logs/XList.php (93%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Rows/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases/Tables}/Rows/XList.php (94%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/Update.php (95%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/Usage/Get.php (92%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Tables/XList.php (94%) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php similarity index 97% rename from src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php rename to src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php index 83500fa964..eaaa08d237 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php @@ -1,6 +1,6 @@ Date: Thu, 8 May 2025 14:15:00 +0530 Subject: [PATCH 066/173] add: indexes api to tables. --- app/config/errors.php | 27 +++ app/controllers/general.php | 8 +- app/init/constants.php | 2 + src/Appwrite/Extend/Exception.php | 7 + .../Http/Databases/Collections/Action.php | 8 - .../Collections/Attributes/Action.php | 14 +- .../Collections/Attributes/Delete.php | 2 +- .../Databases/Collections/Indexes/Action.php | 163 ++++++++++++++++++ .../Databases/Collections/Indexes/Create.php | 106 +++++++----- .../Databases/Collections/Indexes/Delete.php | 64 ++++--- .../Databases/Collections/Indexes/Get.php | 33 ++-- .../Databases/Collections/Indexes/XList.php | 38 ++-- .../Http/Databases/Tables/Indexes/Create.php | 72 ++++++++ .../Http/Databases/Tables/Indexes/Delete.php | 71 ++++++++ .../Http/Databases/Tables/Indexes/Get.php | 60 +++++++ .../Http/Databases/Tables/Indexes/XList.php | 60 +++++++ 16 files changed, 624 insertions(+), 111 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php diff --git a/app/config/errors.php b/app/config/errors.php index 182b6286e8..de26b077fc 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -903,6 +903,33 @@ return [ 'code' => 409, ], + /** Column Indexes, same as Indexes but with different type */ + Exception::COLUMN_INDEX_NOT_FOUND => [ + 'name' => Exception::COLUMN_INDEX_NOT_FOUND, + 'description' => 'Index with the requested ID could not be found.', + 'code' => 404, + ], + Exception::COLUMN_INDEX_LIMIT_EXCEEDED => [ + 'name' => Exception::COLUMN_INDEX_LIMIT_EXCEEDED, + 'description' => 'The maximum number of indexes has been reached.', + 'code' => 400, + ], + Exception::COLUMN_INDEX_ALREADY_EXISTS => [ + 'name' => Exception::COLUMN_INDEX_ALREADY_EXISTS, + 'description' => 'Index with the requested key already exists. Try again with a different key.', + 'code' => 409, + ], + Exception::COLUMN_INDEX_INVALID => [ + 'name' => Exception::COLUMN_INDEX_INVALID, + 'description' => 'Index invalid.', + 'code' => 400, + ], + Exception::COLUMN_INDEX_DEPENDENCY => [ + 'name' => Exception::COLUMN_INDEX_DEPENDENCY, + 'description' => 'Column cannot be renamed or deleted. Please remove the associated index first.', + 'code' => 409, + ], + /** Project Errors */ Exception::PROJECT_NOT_FOUND => [ 'name' => Exception::PROJECT_NOT_FOUND, diff --git a/app/controllers/general.php b/app/controllers/general.php index da68d4758c..b7ee606c69 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1216,7 +1216,13 @@ App::error() ); break; case 'Utopia\Database\Exception\Dependency': - $error = new AppwriteException(AppwriteException::INDEX_DEPENDENCY, null, previous: $error); + $error = new AppwriteException( + $isTablesAPI + ? AppwriteException::COLUMN_INDEX_DEPENDENCY + : AppwriteException::INDEX_DEPENDENCY, + null, + previous: $error + ); break; } diff --git a/app/init/constants.php b/app/init/constants.php index 29a861c45a..c3b6d4a058 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -271,7 +271,9 @@ const TOKENS_RESOURCE_TYPE_DATABASES = 'databases'; const DATABASE_ROWS_CONTEXT = 'row'; const DATABASE_TABLES_CONTEXT = 'table'; const DATABASE_COLUMNS_CONTEXT = 'column'; +const DATABASE_INDEX_CONTEXT = 'index'; const DATABASE_DOCUMENTS_CONTEXT = 'document'; const DATABASE_ATTRIBUTES_CONTEXT = 'attribute'; const DATABASE_COLLECTIONS_CONTEXT = 'collection'; +const DATABASE_COLUMN_INDEX_CONTEXT = 'columnIndex'; diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 15b964577d..38a562571c 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -253,6 +253,13 @@ class Exception extends \Exception public const INDEX_INVALID = 'index_invalid'; public const INDEX_DEPENDENCY = 'index_dependency'; + /** Column Indexes */ + public const COLUMN_INDEX_NOT_FOUND = 'column_index_not_found'; + public const COLUMN_INDEX_LIMIT_EXCEEDED = 'column_index_limit_exceeded'; + public const COLUMN_INDEX_ALREADY_EXISTS = 'column_index_already_exists'; + public const COLUMN_INDEX_INVALID = 'column_index_invalid'; + public const COLUMN_INDEX_DEPENDENCY = 'column_index_dependency'; + /** Projects */ public const PROJECT_NOT_FOUND = 'project_not_found'; public const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php index eaaa08d237..abec90f1a6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php @@ -5,12 +5,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections; use Appwrite\Extend\Exception; use Utopia\Platform\Action as UtopiaAction; -/** - * Abstract base action for Collection and Table database routes. - * - * Provides shared utilities to determine API type, response model, - * SDK group, and context-specific exceptions. - */ abstract class Action extends UtopiaAction { /** @@ -39,8 +33,6 @@ abstract class Action extends UtopiaAction /** * Get the current API context. - * - * @throws \Exception if context has not been set. */ final protected function getContext(): string { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 1c779f4d3b..fff070d58f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -42,7 +42,7 @@ abstract class Action extends UtopiaAction final protected function setContext(string $context): void { if (!\in_array($context, [DATABASE_COLUMNS_CONTEXT, DATABASE_ATTRIBUTES_CONTEXT], true)) { - throw new \InvalidArgumentException("Invalid context '{$context}'. Use `DATABASE_COLUMNS_CONTEXT` or `DATABASE_ATTRIBUTES_CONTEXT`"); + throw new \InvalidArgumentException("Invalid context '$context'. Use `DATABASE_COLUMNS_CONTEXT` or `DATABASE_ATTRIBUTES_CONTEXT`"); } $this->context = $context; @@ -112,6 +112,16 @@ abstract class Action extends UtopiaAction : Exception::COLUMN_NOT_FOUND; } + /** + * Get the appropriate not found exception. + */ + final protected function getIndexDependencyException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_DEPENDENCY + : Exception::COLUMN_INDEX_DEPENDENCY; + } + /** * Get the appropriate already exists exception. */ @@ -275,7 +285,7 @@ abstract class Action extends UtopiaAction if (!empty($format)) { if (!Structure::hasFormat($format, $type)) { - throw new Exception($this->getFormatUnsupportedException(), "Format {$format} not available for {$type} columns."); + throw new Exception($this->getFormatUnsupportedException(), "Format $format not available for $type columns."); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 9597070be6..444fa41a7f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -91,7 +91,7 @@ class Delete extends Action $dbForProject->getAdapter()->getSupportForCastIndexArray(), ); if (!$validator->isValid($attribute)) { - throw new Exception(Exception::INDEX_DEPENDENCY); + throw new Exception($this->getIndexDependencyException()); } if ($attribute->getAttribute('status') === 'available') { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php new file mode 100644 index 0000000000..102acdb639 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -0,0 +1,163 @@ +context = $context; + } + + /** + * Get the current API's parent context. + */ + final protected function getParentContext(): string + { + return $this->getContext() === DATABASE_INDEX_CONTEXT + ? DATABASE_ATTRIBUTES_CONTEXT + : DATABASE_COLUMNS_CONTEXT; + } + + /** + * Get the current API context. + */ + final protected function getContext(): string + { + return $this->context; + } + + /** + * Get the correct grand parent param key (e.g. `tableId` or `collectionId`) + */ + final protected function getGrandParentEventsParamKey(): string + { + return $this->isCollectionsAPI() ? 'collectionId' : 'tableId'; + } + + /** + * Get the key used in event parameters. + */ + final protected function getEventsParamKey(): string + { + return 'indexId'; + } + + /** + * Determine if the current action is for the Collections API. + */ + final protected function isCollectionsAPI(): bool + { + return $this->getParentContext() === DATABASE_ATTRIBUTES_CONTEXT; + } + + /** + * Get the SDK group name for the current action. + */ + final protected function getSdkGroup(): string + { + return 'indexes'; + } + + /** + * Get the exception to throw when the parent is unknown. + */ + final protected function getParentUnknownException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_UNKNOWN + : Exception::COLUMN_UNKNOWN; + } + + /** + * Get the appropriate grandparent level not found exception. + */ + final protected function getGrantParentNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_NOT_FOUND + : Exception::TABLE_NOT_FOUND; + } + + /** + * Get the appropriate not found exception. + */ + final protected function getNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_NOT_FOUND + : Exception::COLUMN_INDEX_NOT_FOUND; + } + + /** + * Get the exception to throw when the parent type is invalid. + */ + final protected function getParentInvalidTypeException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_TYPE_INVALID + : Exception::COLUMN_TYPE_INVALID; + } + + /** + * Get the exception to throw when the index type is invalid. + */ + final protected function getInvalidTypeException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_INVALID + : Exception::COLUMN_INDEX_INVALID; + } + + /** + * Get the exception to throw when the resource already exists. + */ + final protected function getDuplicateException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_ALREADY_EXISTS + : Exception::COLUMN_INDEX_ALREADY_EXISTS; + } + + /** + * Get the exception to throw when the resource limit is exceeded. + */ + final protected function getLimitException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_LIMIT_EXCEEDED + : Exception::COLUMN_INDEX_LIMIT_EXCEEDED; + } + + /** + * Get the exception to throw when the parent attribute/column is not in `available` state. + */ + final protected function getParentNotAvailableException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_NOT_AVAILABLE + : Exception::COLUMN_NOT_AVAILABLE; + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index e9dd4c7092..aa4729e605 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -1,6 +1,6 @@ setHttpMethod(self::HTTP_REQUEST_METHOD_POST) - ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/indexes') ->desc('Create index') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') + ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'index.create') - // TODO: audits table or collections, check the context type if possible, move into another module. - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( namespace: 'databases', - group: 'tables', - name: 'createIndex', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/create-index.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_ACCEPTED, - model: UtopiaResponse::MODEL_INDEX, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', null, new Key(), 'Index Key.') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') - ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') + ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) ->inject('response') ->inject('dbForProject') @@ -74,7 +77,7 @@ class Create extends Action ->callback([$this, 'action']); } - public function action(string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -82,27 +85,28 @@ class Create extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); + if ($collection->isEmpty()) { + // table or collection. + throw new Exception($this->getGrantParentNotFoundException()); } $count = $dbForProject->count('indexes', [ - Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$db->getInternalId()]) ], 61); $limit = $dbForProject->getLimitForIndexes(); if ($count >= $limit) { - throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded'); + throw new Exception($this->getLimitException(), 'Index limit exceeded'); } // Convert Document[] to array of attribute metadata - $oldColumns = \array_map(fn ($a) => $a->getArrayCopy(), $table->getAttribute('attributes')); + $oldAttributes = \array_map(fn ($a) => $a->getArrayCopy(), $collection->getAttribute('attributes')); - $oldColumns[] = [ + $oldAttributes[] = [ 'key' => '$id', 'type' => Database::VAR_STRING, 'status' => 'available', @@ -112,7 +116,7 @@ class Create extends Action 'size' => Database::LENGTH_KEY ]; - $oldColumns[] = [ + $oldAttributes[] = [ 'key' => '$createdAt', 'type' => Database::VAR_DATETIME, 'status' => 'available', @@ -123,7 +127,7 @@ class Create extends Action 'size' => 0 ]; - $oldColumns[] = [ + $oldAttributes[] = [ 'key' => '$updatedAt', 'type' => Database::VAR_DATETIME, 'status' => 'available', @@ -137,25 +141,27 @@ class Create extends Action // lengths hidden by default $lengths = []; - foreach ($columns as $i => $column) { + $contextType = $this->getParentContext(); + foreach ($attributes as $i => $attribute) { // find attribute metadata in collection document - $columnIndex = \array_search($column, array_column($oldColumns, 'key')); + $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); - if ($columnIndex === false) { - throw new Exception(Exception::COLUMN_UNKNOWN, 'Unknown column: ' . $column . '. Verify the column name or create the column.'); + if ($attributeIndex === false) { + throw new Exception($this->getParentUnknownException(), "Unknown $contextType: " . $attribute . ". Verify the $contextType name or create the $contextType."); } - $columnStatus = $oldColumns[$columnIndex]['status']; - $columnType = $oldColumns[$columnIndex]['type']; - $columnArray = $oldColumns[$columnIndex]['array'] ?? false; + $columnStatus = $oldAttributes[$attributeIndex]['status']; + $columnType = $oldAttributes[$attributeIndex]['type']; + $columnArray = $oldAttributes[$attributeIndex]['array'] ?? false; if ($columnType === Database::VAR_RELATIONSHIP) { - throw new Exception(Exception::COLUMN_TYPE_INVALID, 'Cannot create an index for a relationship column: ' . $oldColumns[$columnIndex]['key']); + throw new Exception($this->getParentInvalidTypeException(), "Cannot create an index for a relationship $contextType: " . $oldAttributes[$attributeIndex]['key']); } // ensure attribute is available if ($columnStatus !== 'available') { - throw new Exception(Exception::COLUMN_NOT_AVAILABLE, 'Column not available: ' . $oldColumns[$columnIndex]['key']); + $contextType = ucfirst($contextType); + throw new Exception($this->getParentNotAvailableException(), "$contextType not available: " . $oldAttributes[$attributeIndex]['key']); } $lengths[$i] = null; @@ -167,51 +173,59 @@ class Create extends Action } $index = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key), + '$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), 'key' => $key, 'status' => 'processing', // processing, available, failed, deleting, stuck 'databaseInternalId' => $db->getInternalId(), 'databaseId' => $databaseId, - 'collectionInternalId' => $table->getInternalId(), - 'collectionId' => $tableId, + 'collectionInternalId' => $collection->getInternalId(), + 'collectionId' => $collectionId, 'type' => $type, - 'attributes' => $columns, + 'attributes' => $attributes, 'lengths' => $lengths, 'orders' => $orders, ]); $validator = new IndexValidator( - $table->getAttribute('attributes'), + $collection->getAttribute('attributes'), $dbForProject->getAdapter()->getMaxIndexLength(), $dbForProject->getAdapter()->getInternalIndexesKeys(), ); if (!$validator->isValid($index)) { - throw new Exception(Exception::INDEX_INVALID, $validator->getDescription()); + throw new Exception($this->getInvalidTypeException(), $validator->getDescription()); } try { $index = $dbForProject->createDocument('indexes', $index); } catch (DuplicateException) { - throw new Exception(Exception::INDEX_ALREADY_EXISTS); + throw new Exception($this->getDuplicateException()); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) - ->setDatabase($db) - ->setTable($table) - ->setRow($index); + ->setDatabase($db); + + if ($this->isCollectionsAPI()) { + $queueForDatabase + ->setCollection($collection) + ->setDocument($index); + } else { + $queueForDatabase + ->setTable($collection) + ->setRow($index); + } $queueForEvents + ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('indexId', $index->getId()) - ->setContext('table', $table) - ->setContext('database', $db); + ->setParam($this->getEventsParamKey(), $index->getId()) + ->setParam($this->getGrandParentEventsParamKey(), $collection->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) - ->dynamic($index, UtopiaResponse::MODEL_INDEX); + ->dynamic($index, $this->getResponseModel()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index dd7c4bd5e7..157e4ef7de 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -1,6 +1,6 @@ setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) - ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->desc('Delete index') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') + ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update') ->label('audits.event', 'index.delete') - // TODO: audits table or collections, check the context type if possible - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( namespace: 'databases', - group: 'indexes', - name: 'deleteIndex', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/delete-index.md', auth: [AuthType::KEY], responses: [ @@ -55,7 +62,7 @@ class Delete extends Action contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') @@ -64,23 +71,24 @@ class Delete extends Action ->callback([$this, 'action']); } - public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId); + $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); + if ($collection->isEmpty()) { + // table or collection. + throw new Exception($this->getGrantParentNotFoundException()); } - $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key); + $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if (empty($index->getId())) { - throw new Exception(Exception::INDEX_NOT_FOUND); + throw new Exception($this->getNotFoundException()); } // Only update status if removing available index @@ -88,21 +96,29 @@ class Delete extends Action $index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) - ->setDatabase($db) - ->setTable($table) - ->setRow($index); + ->setDatabase($db); + + if ($this->isCollectionsAPI()) { + $queueForDatabase + ->setCollection($collection) + ->setDocument($index); + } else { + $queueForDatabase + ->setTable($collection) + ->setRow($index); + } $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('tableId', $table->getId()) - ->setParam('indexId', $index->getId()) - ->setContext('table', $table) ->setContext('database', $db) - ->setPayload($response->output($index, UtopiaResponse::MODEL_INDEX)); + ->setParam('databaseId', $databaseId) + ->setParam($this->getEventsParamKey(), $index->getId()) + ->setPayload($response->output($index, $this->getResponseModel())) + ->setParam($this->getGrandParentEventsParamKey(), $collection->getId()) + ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index a89819d844..3e8a67b395 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -1,6 +1,6 @@ setHttpMethod(self::HTTP_REQUEST_METHOD_GET) - ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->desc('Get index') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'indexes', - name: 'getIndex', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/get-index.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_INDEX, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') ->callback([$this, 'action']); } - public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); + if ($collection->isEmpty()) { + // table or collection. + throw new Exception($this->getGrantParentNotFoundException()); } - $index = $table->find('key', $key, 'indexes'); + $index = $collection->find('key', $key, 'indexes'); if (empty($index)) { - throw new Exception(Exception::INDEX_NOT_FOUND); + throw new Exception($this->getNotFoundException()); } - $response->dynamic($index, UtopiaResponse::MODEL_INDEX); + $response->dynamic($index, $this->getResponseModel()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index b11806cfd0..45627654dc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -1,6 +1,6 @@ setHttpMethod(self::HTTP_REQUEST_METHOD_GET) - ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/indexes') ->desc('List indexes') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: 'indexes', - name: 'listIndexes', + group: $this->getSdkGroup(), + name: self::getName(), description: '/docs/references/databases/list-indexes.md', auth: [AuthType::KEY], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, - model: UtopiaResponse::MODEL_INDEX_LIST, + model: $this->getResponseModel(), ) ], contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') ->callback([$this, 'action']); } - public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void { /** @var Document $database */ $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -69,10 +73,11 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - if ($table->isEmpty()) { - throw new Exception(Exception::TABLE_NOT_FOUND); + if ($collection->isEmpty()) { + // table or collection. + throw new Exception($this->getGrantParentNotFoundException()); } $queries = Query::parseQueries($queries); @@ -80,7 +85,7 @@ class XList extends Action \array_push( $queries, Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$tableId]), + Query::equal('collectionId', [$collectionId]), ); /** @@ -99,7 +104,7 @@ class XList extends Action $indexId = $cursor->getValue(); $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ - Query::equal('collectionInternalId', [$table->getInternalId()]), + Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$indexId]), Query::limit(1) @@ -117,12 +122,15 @@ class XList extends Action $total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT); $indexes = $dbForProject->find('indexes', $queries); } catch (OrderException $e) { - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); + $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; + $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; + $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); } $response->dynamic(new Document([ 'total' => $total, 'indexes' => $indexes, - ]), UtopiaResponse::MODEL_INDEX_LIST); + ]), $this->getResponseModel()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php new file mode 100644 index 0000000000..844a317de7 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -0,0 +1,72 @@ +setContext(DATABASE_COLUMN_INDEX_CONTEXT); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/:databaseId/tables/:tables/indexes') + ->desc('Create index') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tables].indexes.[indexId].create') + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'index.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/create-index.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_ACCEPTED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', null, new Key(), 'Index Key.') + ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') + ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') + ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback(function (string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $type, $columns, $orders, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php new file mode 100644 index 0000000000..d4185cfcbe --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -0,0 +1,71 @@ +setContext(DATABASE_COLUMN_INDEX_CONTEXT); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->desc('Delete index') + ->groups(['api', 'database']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') + ->label('audits.event', 'index.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/delete-index.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_NOCONTENT, + model: UtopiaResponse::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', '', new Key(), 'Index Key.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + parent::action($databaseId, $tableId, $key, $response, $dbForProject, $queueForDatabase, $queueForEvents); + }); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php new file mode 100644 index 0000000000..ee0432522c --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -0,0 +1,60 @@ +setContext(DATABASE_COLUMN_INDEX_CONTEXT); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') + ->desc('Get index') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/get-index.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('key', null, new Key(), 'Index Key.') + ->inject('response') + ->inject('dbForProject') + ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $tableId, $key, $response, $dbForProject); + }); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php new file mode 100644 index 0000000000..50c1eb11d1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -0,0 +1,60 @@ +setContext(DATABASE_COLUMN_INDEX_CONTEXT); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') + ->desc('List indexes') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/list-indexes.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) + ->inject('response') + ->inject('dbForProject') + ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject) { + parent::action($databaseId, $tableId, $queries, $response, $dbForProject); + }); + } +} From 6fba4a5f08e3b2a7ac9258424c6bf040ee275b20 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 14:20:11 +0530 Subject: [PATCH 067/173] update: sdk namespaces. --- .../Http/Databases/Collections/Attributes/Action.php | 8 ++++++++ .../Databases/Collections/Attributes/Boolean/Create.php | 2 +- .../Databases/Collections/Attributes/Boolean/Update.php | 2 +- .../Databases/Collections/Attributes/Datetime/Create.php | 2 +- .../Databases/Collections/Attributes/Datetime/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Delete.php | 2 +- .../Databases/Collections/Attributes/Email/Create.php | 2 +- .../Databases/Collections/Attributes/Email/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Enum/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Enum/Update.php | 2 +- .../Databases/Collections/Attributes/Float/Create.php | 2 +- .../Databases/Collections/Attributes/Float/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Get.php | 2 +- .../Http/Databases/Collections/Attributes/IP/Create.php | 2 +- .../Http/Databases/Collections/Attributes/IP/Update.php | 2 +- .../Databases/Collections/Attributes/Integer/Create.php | 2 +- .../Databases/Collections/Attributes/Integer/Update.php | 2 +- .../Collections/Attributes/Relationship/Create.php | 2 +- .../Collections/Attributes/Relationship/Update.php | 2 +- .../Databases/Collections/Attributes/String/Create.php | 2 +- .../Databases/Collections/Attributes/String/Update.php | 2 +- .../Http/Databases/Collections/Attributes/URL/Create.php | 2 +- .../Http/Databases/Collections/Attributes/URL/Update.php | 2 +- .../Http/Databases/Collections/Attributes/XList.php | 2 +- .../Http/Databases/Collections/Documents/Action.php | 8 ++++++++ .../Http/Databases/Collections/Documents/Create.php | 2 +- .../Http/Databases/Collections/Documents/Delete.php | 2 +- .../Http/Databases/Collections/Documents/Get.php | 2 +- .../Http/Databases/Collections/Documents/Logs/XList.php | 2 +- .../Http/Databases/Collections/Documents/Update.php | 2 +- .../Http/Databases/Collections/Documents/XList.php | 2 +- .../Http/Databases/Collections/Indexes/Action.php | 8 ++++++++ .../Http/Databases/Collections/Indexes/Create.php | 2 +- .../Http/Databases/Collections/Indexes/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Get.php | 2 +- .../Http/Databases/Collections/Indexes/XList.php | 2 +- .../Http/Databases/Tables/Columns/Boolean/Create.php | 2 +- .../Http/Databases/Tables/Columns/Boolean/Update.php | 2 +- .../Http/Databases/Tables/Columns/Datetime/Create.php | 2 +- .../Http/Databases/Tables/Columns/Datetime/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Delete.php | 2 +- .../Http/Databases/Tables/Columns/Email/Create.php | 2 +- .../Http/Databases/Tables/Columns/Email/Update.php | 2 +- .../Http/Databases/Tables/Columns/Enum/Create.php | 2 +- .../Http/Databases/Tables/Columns/Enum/Update.php | 2 +- .../Http/Databases/Tables/Columns/Float/Create.php | 2 +- .../Http/Databases/Tables/Columns/Float/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Get.php | 2 +- .../Databases/Http/Databases/Tables/Columns/IP/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/IP/Update.php | 2 +- .../Http/Databases/Tables/Columns/Integer/Create.php | 2 +- .../Http/Databases/Tables/Columns/Integer/Update.php | 2 +- .../Http/Databases/Tables/Columns/Relationship/Create.php | 2 +- .../Http/Databases/Tables/Columns/Relationship/Update.php | 2 +- .../Http/Databases/Tables/Columns/String/Create.php | 2 +- .../Http/Databases/Tables/Columns/String/Update.php | 2 +- .../Http/Databases/Tables/Columns/URL/Create.php | 2 +- .../Http/Databases/Tables/Columns/URL/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/XList.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Create.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Delete.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Get.php | 2 +- .../Databases/Http/Databases/Tables/Indexes/Create.php | 2 +- .../Databases/Http/Databases/Tables/Indexes/Delete.php | 2 +- .../Databases/Http/Databases/Tables/Indexes/Get.php | 2 +- .../Databases/Http/Databases/Tables/Indexes/XList.php | 2 +- .../Databases/Http/Databases/Tables/Logs/XList.php | 2 +- .../Databases/Http/Databases/Tables/Rows/Create.php | 2 +- .../Databases/Http/Databases/Tables/Rows/Delete.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Get.php | 2 +- .../Databases/Http/Databases/Tables/Rows/Logs/XList.php | 2 +- .../Databases/Http/Databases/Tables/Rows/Update.php | 2 +- .../Databases/Http/Databases/Tables/Rows/XList.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Usage/Get.php | 2 +- .../Modules/Databases/Http/Databases/Tables/XList.php | 2 +- 76 files changed, 97 insertions(+), 73 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index fff070d58f..b683ebd64c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -76,6 +76,14 @@ abstract class Action extends UtopiaAction return $this->isCollectionsAPI() ? 'attributes' : 'columns'; } + /** + * Get the SDK namespace for the current action. + */ + final protected function getSdkNamespace(): string + { + return $this->isCollectionsAPI() ? 'collections' : 'tables'; + } + /** * Get the correct parent param key (e.g. `tableId` or `collectionId`) */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php index d70188e621..56c0753f08 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php @@ -44,7 +44,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-boolean-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php index 73739e1d91..c8aa734570 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php @@ -44,7 +44,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-boolean-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php index 50e20a5c56..e3e0cc359b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php @@ -45,7 +45,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-datetime-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php index afac2904ff..eac9ba278c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-datetime-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 444fa41a7f..2432ac5fd0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -46,7 +46,7 @@ class Delete extends Action ->label('audits.event', 'attribute.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php index 2d64c00393..80309609b8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php @@ -45,7 +45,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-email-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php index fcf5ab7fca..5c35084508 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-email-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php index ea7370e493..8e4ad25f75 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php @@ -47,7 +47,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-attribute-enum.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php index b8e855de99..be04a1ef9a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php @@ -46,7 +46,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-enum-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php index 821f3134ab..4e3a8adf75 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php @@ -47,7 +47,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-float-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php index 3ef7f5cae4..13a90310e1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-float-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index 226dcefa2b..e04a89295b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -49,7 +49,7 @@ class Get extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php index cb472fc2c8..4bf5edc09d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php @@ -45,7 +45,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-ip-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php index 233672be03..380fa01123 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-ip-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php index 4bd45adf48..dc28e41264 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php @@ -47,7 +47,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-integer-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php index e8197ba0d9..9e864e8b62 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-integer-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index 5d799c11c9..193e3eaa09 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -47,7 +47,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-relationship-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php index 5375135c28..b489f92c3d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php @@ -43,7 +43,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-relationship-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php index 6c8ec7e9e1..6463d2cedf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php @@ -48,7 +48,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-string-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php index 059ad1e28c..e746edb766 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php @@ -47,7 +47,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-string-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php index f7c948d43e..a802bbf8af 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php @@ -45,7 +45,7 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-url-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php index 8fa53d79b4..9ca1f372dc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-url-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index c89ad043f3..29e8d1d6b9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -43,7 +43,7 @@ class XList extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-attributes.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 2eabd376cf..b9a3247f96 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -59,6 +59,14 @@ abstract class Action extends UtopiaAction return $this->isCollectionsAPI() ? 'documents' : 'rows'; } + /** + * Get the SDK namespace for the current action. + */ + final protected function getSdkNamespace(): string + { + return $this->isCollectionsAPI() ? 'collections' : 'tables'; + } + /** * Get the correct parent param key (e.g. `tableId` or `collectionId`) */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 6522ff377c..6db7e802f3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -58,7 +58,7 @@ class Create extends Action ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index dcca07eed4..2f0c4e76db 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -53,7 +53,7 @@ class Delete extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index 29cfb19013..f56ab40126 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -46,7 +46,7 @@ class Get extends Action ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 6c37d3e238..bb24d41b30 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -50,7 +50,7 @@ class XList extends Action ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: 'logs', name: self::getName(), description: '/docs/references/databases/get-document-logs.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 5f6935abcb..4ad045a998 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -57,7 +57,7 @@ class Update extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 3ddbf8df7f..7b4f5855e2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -47,7 +47,7 @@ class XList extends Action ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-documents.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index 102acdb639..6186139bfb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -81,6 +81,14 @@ abstract class Action extends UtopiaAction return 'indexes'; } + /** + * Get the SDK namespace for the current action. + */ + final protected function getSdkNamespace(): string + { + return $this->isCollectionsAPI() ? 'collections' : 'tables'; + } + /** * Get the exception to throw when the parent is unknown. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index aa4729e605..fe21025740 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -51,7 +51,7 @@ class Create extends Action ->label('audits.event', 'index.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-index.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index 157e4ef7de..2d57a8a5f0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -48,7 +48,7 @@ class Delete extends Action ->label('audits.event', 'index.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-index.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index 3e8a67b395..fa10406f2a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -39,7 +39,7 @@ class Get extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-index.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index 45627654dc..733e6854b2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -43,7 +43,7 @@ class XList extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-indexes.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php index 740bd89c0b..6f48afe57f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php @@ -45,7 +45,7 @@ class Create extends BooleanCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-boolean-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php index a2254a5734..485a9587e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php @@ -46,7 +46,7 @@ class Update extends BooleanUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-boolean-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php index ef0c61e1f4..1a5f6f65e1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php @@ -46,7 +46,7 @@ class Create extends DatetimeCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-datetime-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php index 14e57d6a70..1eb13852e7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php @@ -47,7 +47,7 @@ class Update extends DatetimeUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-datetime-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php index 064e277942..10604029dd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php @@ -46,7 +46,7 @@ class Delete extends AttributesDelete ->label('audits.event', 'column.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php index 635b673e03..2d7b65931f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php @@ -46,7 +46,7 @@ class Create extends EmailCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-email-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php index 0162633ed9..a067334a9d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php @@ -47,7 +47,7 @@ class Update extends EmailUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-email-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php index 942d49d86a..8fdb689b46 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php @@ -47,7 +47,7 @@ class Create extends EnumCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-attribute-enum.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php index 2018170105..85e1c4e5cc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php @@ -48,7 +48,7 @@ class Update extends EnumUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-enum-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php index 13d26d0331..2ad2c11572 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php @@ -46,7 +46,7 @@ class Create extends FloatCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-float-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php index 55c4c13505..ef6080b86c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php @@ -47,7 +47,7 @@ class Update extends FloatUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-float-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php index db83c3ecbc..42ba23ad3a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php @@ -50,7 +50,7 @@ class Get extends AttributesGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php index c0d671183c..34d5ecdfca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php @@ -46,7 +46,7 @@ class Create extends IPCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-ip-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php index e9092c38c5..34cfe97343 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php @@ -47,7 +47,7 @@ class Update extends IPUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-ip-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php index 26868fa667..13aa90b6e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php @@ -46,7 +46,7 @@ class Create extends IntegerCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-integer-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php index fa50515a10..6cf3383795 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php @@ -47,7 +47,7 @@ class Update extends IntegerUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-integer-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index 4693d494c6..2e338ae037 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -46,7 +46,7 @@ class Create extends RelationshipCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-relationship-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php index e110eafc56..1afdd0c56a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php @@ -45,7 +45,7 @@ class Update extends RelationshipUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-relationship-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index 354aef6ff9..e17ac07ccf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -48,7 +48,7 @@ class Create extends StringCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-string-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php index 649d481794..f64d5408eb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php @@ -49,7 +49,7 @@ class Update extends StringUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-string-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php index ce7c5dfe9f..a0f4e2ae8f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php @@ -46,7 +46,7 @@ class Create extends URLCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-url-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php index 298165b2cf..623328e249 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php @@ -47,7 +47,7 @@ class Update extends URLUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-url-attribute.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index 2e41fe86eb..a6b43518b9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -39,7 +39,7 @@ class XList extends AttributesXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-attributes.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 1f349b95ed..6984ff0b5d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -47,7 +47,7 @@ class Create extends CollectionCreate ->label('audits.event', 'table.create') ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index 338d344660..5bce6fa519 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -44,7 +44,7 @@ class Delete extends CollectionDelete ->label('audits.event', 'table.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index ffa1da443e..b4ca31dbe0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -39,7 +39,7 @@ class Get extends CollectionGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index 844a317de7..97ac92d559 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -42,7 +42,7 @@ class Create extends IndexCreate ->label('audits.event', 'index.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-index.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index d4185cfcbe..6a86a4767e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -44,7 +44,7 @@ class Delete extends IndexDelete ->label('audits.event', 'index.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-index.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index ee0432522c..0cc537d960 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -35,7 +35,7 @@ class Get extends IndexGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-index.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index 50c1eb11d1..5a245cf533 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -35,7 +35,7 @@ class XList extends IndexXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-indexes.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 9a6d4c4e8e..8783067f69 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -39,7 +39,7 @@ class XList extends CollectionLogXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-collection-logs.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 26e14b9ca5..1773a4c2ff 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -52,7 +52,7 @@ class Create extends DocumentCreate ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php index c0cc4b0dd1..05a98d6096 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php @@ -53,7 +53,7 @@ class Delete extends DocumentDelete ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php index 0b74f19f41..717cec3721 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php @@ -42,7 +42,7 @@ class Get extends DocumentGet ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php index 66ac69323c..23b70ac19e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php @@ -39,7 +39,7 @@ class XList extends DocumentLogXList ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: 'logs', name: self::getName(), description: '/docs/references/databases/get-document-logs.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index 7148452c74..0a83feee38 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -49,7 +49,7 @@ class Update extends DocumentUpdate ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-document.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php index 62fe6b52b0..f4bdbaa483 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php @@ -42,7 +42,7 @@ class XList extends DocumentXList ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-documents.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index e60fbb4ac8..88b72322c1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -46,7 +46,7 @@ class Update extends CollectionUpdate ->label('audits.event', 'table.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index 304884cece..3b2a40584e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -40,7 +40,7 @@ class Get extends CollectionUsageGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: null, name: self::getName(), description: '/docs/references/databases/get-collection-usage.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index 10913cd491..733703d05f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -41,7 +41,7 @@ class XList extends CollectionXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-collections.md', From 97d1a535df35a3b584b5dc1e28280e5f585131a8 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 15:30:52 +0530 Subject: [PATCH 068/173] fix: missing path :\ --- .../Databases/Http/Databases/Collections/Documents/Update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 4ad045a998..be009b9cd2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -41,11 +41,11 @@ class Update extends Action return UtopiaResponse::MODEL_DOCUMENT; } - public function __construct() { $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->desc('Update document') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update') From d7023b5a6977d7a116ef8dad3439d77504e9dbc1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 15:33:00 +0530 Subject: [PATCH 069/173] fix: namespaces. --- .../Platform/Modules/Databases/Http/Databases/Tables/Create.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Delete.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Get.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Update.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/XList.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 6984ff0b5d..1f349b95ed 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -47,7 +47,7 @@ class Create extends CollectionCreate ->label('audits.event', 'table.create') ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/create-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index 5bce6fa519..338d344660 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -44,7 +44,7 @@ class Delete extends CollectionDelete ->label('audits.event', 'table.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/delete-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index b4ca31dbe0..ffa1da443e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -39,7 +39,7 @@ class Get extends CollectionGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index 88b72322c1..e60fbb4ac8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -46,7 +46,7 @@ class Update extends CollectionUpdate ->label('audits.event', 'table.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/update-collection.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index 733703d05f..10913cd491 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -41,7 +41,7 @@ class XList extends CollectionXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/list-collections.md', From eafa9bb3934671a36594e3e7bd4f9e3bbd3c6d17 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 15:33:41 +0530 Subject: [PATCH 070/173] fix: namespaces, again. --- .../Modules/Databases/Http/Databases/Tables/Logs/XList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 8783067f69..9a6d4c4e8e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -39,7 +39,7 @@ class XList extends CollectionLogXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: $this->getSdkGroup(), name: self::getName(), description: '/docs/references/databases/get-collection-logs.md', From c4a26fbb952ac16076989294c69134923b724781 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 15:37:00 +0530 Subject: [PATCH 071/173] =?UTF-8?q?fix:=20another=20pesky=20sdk=20namespac?= =?UTF-8?q?e=20=F0=9F=A4=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modules/Databases/Http/Databases/Tables/Usage/Get.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index 3b2a40584e..304884cece 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -40,7 +40,7 @@ class Get extends CollectionUsageGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: 'databases', group: null, name: self::getName(), description: '/docs/references/databases/get-collection-usage.md', From 4e83fc856ab2e5debe82edfab3189cd621d6aae0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 15:38:07 +0530 Subject: [PATCH 072/173] update: split service registrations into smaller registries. --- .../Modules/Databases/Services/Http.php | 169 ++---------------- .../Databases/Services/Registry/Base.php | 24 +++ .../Services/Registry/Collections.php | 140 +++++++++++++++ .../Databases/Services/Registry/Databases.php | 31 ++++ .../Databases/Services/Registry/Tables.php | 142 +++++++++++++++ 5 files changed, 347 insertions(+), 159 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Base.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index 26a4aa0942..6c1361b0b1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -2,61 +2,9 @@ namespace Appwrite\Platform\Modules\Databases\Services; -use Appwrite\Platform\Modules\Databases\Http\Collections\Create as CreateCollection; -use Appwrite\Platform\Modules\Databases\Http\Collections\Delete as DeleteCollection; -use Appwrite\Platform\Modules\Databases\Http\Collections\Get as GetCollection; -use Appwrite\Platform\Modules\Databases\Http\Collections\Logs\XList as ListCollectionLogs; -use Appwrite\Platform\Modules\Databases\Http\Collections\Update as UpdateCollection; -use Appwrite\Platform\Modules\Databases\Http\Collections\Usage\Get as GetCollectionUsage; -use Appwrite\Platform\Modules\Databases\Http\Collections\XList as ListCollections; -use Appwrite\Platform\Modules\Databases\Http\Columns\Boolean\Create as CreateBoolean; -use Appwrite\Platform\Modules\Databases\Http\Columns\Boolean\Update as UpdateBoolean; -use Appwrite\Platform\Modules\Databases\Http\Columns\Datetime\Create as CreateDatetime; -use Appwrite\Platform\Modules\Databases\Http\Columns\Datetime\Update as UpdateDatetime; -use Appwrite\Platform\Modules\Databases\Http\Columns\Delete as DeleteColumn; -use Appwrite\Platform\Modules\Databases\Http\Columns\Email\Create as CreateEmail; -use Appwrite\Platform\Modules\Databases\Http\Columns\Email\Update as UpdateEmail; -use Appwrite\Platform\Modules\Databases\Http\Columns\Enum\Create as CreateEnum; -use Appwrite\Platform\Modules\Databases\Http\Columns\Enum\Update as UpdateEnum; -use Appwrite\Platform\Modules\Databases\Http\Columns\Float\Create as CreateFloat; -use Appwrite\Platform\Modules\Databases\Http\Columns\Float\Update as UpdateFloat; -use Appwrite\Platform\Modules\Databases\Http\Columns\Get as GetColumn; -use Appwrite\Platform\Modules\Databases\Http\Columns\Integer\Create as CreateInteger; -use Appwrite\Platform\Modules\Databases\Http\Columns\Integer\Update as UpdateInteger; -use Appwrite\Platform\Modules\Databases\Http\Columns\IP\Create as CreateIP; -use Appwrite\Platform\Modules\Databases\Http\Columns\IP\Update as UpdateIP; -use Appwrite\Platform\Modules\Databases\Http\Columns\Relationship\Create as CreateRelationship; -use Appwrite\Platform\Modules\Databases\Http\Columns\Relationship\Update as UpdateRelationship; -use Appwrite\Platform\Modules\Databases\Http\Columns\String\Create as CreateString; -use Appwrite\Platform\Modules\Databases\Http\Columns\String\Update as UpdateString; -use Appwrite\Platform\Modules\Databases\Http\Columns\URL\Create as CreateURL; -use Appwrite\Platform\Modules\Databases\Http\Columns\URL\Update as UpdateURL; -use Appwrite\Platform\Modules\Databases\Http\Columns\XList as ListColumns; -use Appwrite\Platform\Modules\Databases\Http\Databases\Create as CreateDatabase; -use Appwrite\Platform\Modules\Databases\Http\Databases\Delete as DeleteDatabase; -use Appwrite\Platform\Modules\Databases\Http\Databases\Get as GetDatabase; -use Appwrite\Platform\Modules\Databases\Http\Databases\Logs\XList as ListDatabaseLogs; -use Appwrite\Platform\Modules\Databases\Http\Databases\Update as UpdateDatabase; -use Appwrite\Platform\Modules\Databases\Http\Databases\Usage\Get as GetDatabaseUsage; -use Appwrite\Platform\Modules\Databases\Http\Databases\Usage\XList as ListDatabaseUsage; -use Appwrite\Platform\Modules\Databases\Http\Databases\XList as ListDatabases; -use Appwrite\Platform\Modules\Databases\Http\Indexes\Create as CreateIndex; -use Appwrite\Platform\Modules\Databases\Http\Indexes\Delete as DeleteIndex; -use Appwrite\Platform\Modules\Databases\Http\Indexes\Get as GetIndex; -use Appwrite\Platform\Modules\Databases\Http\Indexes\XList as ListIndexes; -use Appwrite\Platform\Modules\Databases\Http\Rows\Create as CreateRow; -use Appwrite\Platform\Modules\Databases\Http\Rows\Delete as DeleteRow; -use Appwrite\Platform\Modules\Databases\Http\Rows\Get as GetRow; -use Appwrite\Platform\Modules\Databases\Http\Rows\Logs\XList as ListRowLogs; -use Appwrite\Platform\Modules\Databases\Http\Rows\Update as UpdateRow; -use Appwrite\Platform\Modules\Databases\Http\Rows\XList as ListRows; -use Appwrite\Platform\Modules\Databases\Http\Tables\Create as CreateTable; -use Appwrite\Platform\Modules\Databases\Http\Tables\Delete as DeleteTable; -use Appwrite\Platform\Modules\Databases\Http\Tables\Get as GetTable; -use Appwrite\Platform\Modules\Databases\Http\Tables\Logs\XList as ListTableLogs; -use Appwrite\Platform\Modules\Databases\Http\Tables\Update as UpdateTable; -use Appwrite\Platform\Modules\Databases\Http\Tables\Usage\Get as GetTableUsage; -use Appwrite\Platform\Modules\Databases\Http\Tables\XList as ListTables; +use Appwrite\Platform\Modules\Databases\Services\Registry\Collections as CollectionsRegistry; +use Appwrite\Platform\Modules\Databases\Services\Registry\Databases as DatabasesRegistry; +use Appwrite\Platform\Modules\Databases\Services\Registry\Tables as TablesRegistry; use Utopia\Platform\Service; class Http extends Service @@ -65,109 +13,12 @@ class Http extends Service { $this->type = Service::TYPE_HTTP; - $this->registerDatabaseActions(); - $this->registerCollectionAndTableActions(); - $this->registerColumnActions(); - $this->registerIndexActions(); - $this->registerRowActions(); - } - - private function registerDatabaseActions(): void - { - $this->addAction(CreateDatabase::getName(), new CreateDatabase()); - $this->addAction(GetDatabase::getName(), new GetDatabase()); - $this->addAction(UpdateDatabase::getName(), new UpdateDatabase()); - $this->addAction(DeleteDatabase::getName(), new DeleteDatabase()); - $this->addAction(ListDatabases::getName(), new ListDatabases()); - $this->addAction(ListDatabaseLogs::getName(), new ListDatabaseLogs()); - $this->addAction(GetDatabaseUsage::getName(), new GetDatabaseUsage()); - $this->addAction(ListDatabaseUsage::getName(), new ListDatabaseUsage()); - } - - private function registerCollectionAndTableActions(): void - { - // Collections - $this->addAction(CreateCollection::getName(), new CreateCollection()); - $this->addAction(GetCollection::getName(), new GetCollection()); - $this->addAction(UpdateCollection::getName(), new UpdateCollection()); - $this->addAction(DeleteCollection::getName(), new DeleteCollection()); - $this->addAction(ListCollections::getName(), new ListCollections()); - $this->addAction(ListCollectionLogs::getName(), new ListCollectionLogs()); - $this->addAction(GetCollectionUsage::getName(), new GetCollectionUsage()); - - // Tables - $this->addAction(CreateTable::getName(), new CreateTable()); - $this->addAction(GetTable::getName(), new GetTable()); - $this->addAction(UpdateTable::getName(), new UpdateTable()); - $this->addAction(DeleteTable::getName(), new DeleteTable()); - $this->addAction(ListTables::getName(), new ListTables()); - $this->addAction(ListTableLogs::getName(), new ListTableLogs()); - $this->addAction(GetTableUsage::getName(), new GetTableUsage()); - } - - private function registerColumnActions(): void - { - // Column top level actions - $this->addAction(GetColumn::getName(), new GetColumn()); - $this->addAction(DeleteColumn::getName(), new DeleteColumn()); - $this->addAction(ListColumns::getName(), new ListColumns()); - - // Column: Boolean - $this->addAction(CreateBoolean::getName(), new CreateBoolean()); - $this->addAction(UpdateBoolean::getName(), new UpdateBoolean()); - - // Column: Datetime - $this->addAction(CreateDatetime::getName(), new CreateDatetime()); - $this->addAction(UpdateDatetime::getName(), new UpdateDatetime()); - - // Column: Email - $this->addAction(CreateEmail::getName(), new CreateEmail()); - $this->addAction(UpdateEmail::getName(), new UpdateEmail()); - - // Column: Enum - $this->addAction(CreateEnum::getName(), new CreateEnum()); - $this->addAction(UpdateEnum::getName(), new UpdateEnum()); - - // Column: Float - $this->addAction(CreateFloat::getName(), new CreateFloat()); - $this->addAction(UpdateFloat::getName(), new UpdateFloat()); - - // Column: Integer - $this->addAction(CreateInteger::getName(), new CreateInteger()); - $this->addAction(UpdateInteger::getName(), new UpdateInteger()); - - // Column: IP - $this->addAction(CreateIP::getName(), new CreateIP()); - $this->addAction(UpdateIP::getName(), new UpdateIP()); - - // Column: Relationship - $this->addAction(CreateRelationship::getName(), new CreateRelationship()); - $this->addAction(UpdateRelationship::getName(), new UpdateRelationship()); - - // Column: String - $this->addAction(CreateString::getName(), new CreateString()); - $this->addAction(UpdateString::getName(), new UpdateString()); - - // Column: URL - $this->addAction(CreateURL::getName(), new CreateURL()); - $this->addAction(UpdateURL::getName(), new UpdateURL()); - } - - private function registerIndexActions(): void - { - $this->addAction(CreateIndex::getName(), new CreateIndex()); - $this->addAction(GetIndex::getName(), new GetIndex()); - $this->addAction(DeleteIndex::getName(), new DeleteIndex()); - $this->addAction(ListIndexes::getName(), new ListIndexes()); - } - - private function registerRowActions(): void - { - $this->addAction(CreateRow::getName(), new CreateRow()); - $this->addAction(GetRow::getName(), new GetRow()); - $this->addAction(UpdateRow::getName(), new UpdateRow()); - $this->addAction(DeleteRow::getName(), new DeleteRow()); - $this->addAction(ListRows::getName(), new ListRows()); - $this->addAction(ListRowLogs::getName(), new ListRowLogs()); + foreach ([ + DatabasesRegistry::class, + CollectionsRegistry::class, + TablesRegistry::class, + ] as $registrar) { + new $registrar($this); + } } } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Base.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Base.php new file mode 100644 index 0000000000..43bc4b2959 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Base.php @@ -0,0 +1,24 @@ +register($service); + } + + /** + * Register all HTTP actions related to this module. + */ + abstract protected function register(Service $service): void; +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php new file mode 100644 index 0000000000..7b629bca44 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -0,0 +1,140 @@ +registerCollectionActions($service); + $this->registerDocumentActions($service); + $this->registerAttributeActions($service); + $this->registerIndexActions($service); + } + + private function registerCollectionActions(Service $service): void + { + $service->addAction(CreateCollection::getName(), new CreateCollection()); + $service->addAction(GetCollection::getName(), new GetCollection()); + $service->addAction(UpdateCollection::getName(), new UpdateCollection()); + $service->addAction(DeleteCollection::getName(), new DeleteCollection()); + $service->addAction(ListCollections::getName(), new ListCollections()); + $service->addAction(ListCollectionLogs::getName(), new ListCollectionLogs()); + $service->addAction(GetCollectionUsage::getName(), new GetCollectionUsage()); + } + + private function registerDocumentActions(Service $service): void + { + $service->addAction(CreateDocument::getName(), new CreateDocument()); + $service->addAction(GetDocument::getName(), new GetDocument()); + $service->addAction(UpdateDocument::getName(), new UpdateDocument()); + $service->addAction(DeleteDocument::getName(), new DeleteDocument()); + $service->addAction(ListDocuments::getName(), new ListDocuments()); + } + + private function registerAttributeActions(Service $service): void + { + // Attribute top-level actions + $service->addAction(GetAttribute::getName(), new GetAttribute()); + $service->addAction(DeleteAttribute::getName(), new DeleteAttribute()); + $service->addAction(ListAttributes::getName(), new ListAttributes()); + + // Attribute: Boolean + $service->addAction(CreateBooleanAttribute::getName(), new CreateBooleanAttribute()); + $service->addAction(UpdateBooleanAttribute::getName(), new UpdateBooleanAttribute()); + + // Attribute: Datetime + $service->addAction(CreateDatetimeAttribute::getName(), new CreateDatetimeAttribute()); + $service->addAction(UpdateDatetimeAttribute::getName(), new UpdateDatetimeAttribute()); + + // Attribute: Email + $service->addAction(CreateEmailAttribute::getName(), new CreateEmailAttribute()); + $service->addAction(UpdateEmailAttribute::getName(), new UpdateEmailAttribute()); + + // Attribute: Enum + $service->addAction(CreateEnumAttribute::getName(), new CreateEnumAttribute()); + $service->addAction(UpdateEnumAttribute::getName(), new UpdateEnumAttribute()); + + // Attribute: Float + $service->addAction(CreateFloatAttribute::getName(), new CreateFloatAttribute()); + $service->addAction(UpdateFloatAttribute::getName(), new UpdateFloatAttribute()); + + // Attribute: Integer + $service->addAction(CreateIntegerAttribute::getName(), new CreateIntegerAttribute()); + $service->addAction(UpdateIntegerAttribute::getName(), new UpdateIntegerAttribute()); + + // Attribute: IP + $service->addAction(CreateIPAttribute::getName(), new CreateIPAttribute()); + $service->addAction(UpdateIPAttribute::getName(), new UpdateIPAttribute()); + + // Attribute: Relationship + $service->addAction(CreateRelationshipAttribute::getName(), new CreateRelationshipAttribute()); + $service->addAction(UpdateRelationshipAttribute::getName(), new UpdateRelationshipAttribute()); + + // Attribute: String + $service->addAction(CreateStringAttribute::getName(), new CreateStringAttribute()); + $service->addAction(UpdateStringAttribute::getName(), new UpdateStringAttribute()); + + // Attribute: URL + $service->addAction(CreateURLAttribute::getName(), new CreateURLAttribute()); + $service->addAction(UpdateURLAttribute::getName(), new UpdateURLAttribute()); + } + + private function registerIndexActions(Service $service): void + { + $service->addAction(CreateIndex::getName(), new CreateIndex()); + $service->addAction(GetIndex::getName(), new GetIndex()); + $service->addAction(DeleteIndex::getName(), new DeleteIndex()); + $service->addAction(ListIndexes::getName(), new ListIndexes()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php new file mode 100644 index 0000000000..81c9174253 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php @@ -0,0 +1,31 @@ +addAction(CreateDatabase::getName(), new CreateDatabase()); + $service->addAction(GetDatabase::getName(), new GetDatabase()); + $service->addAction(UpdateDatabase::getName(), new UpdateDatabase()); + $service->addAction(DeleteDatabase::getName(), new DeleteDatabase()); + $service->addAction(ListDatabases::getName(), new ListDatabases()); + $service->addAction(ListDatabaseLogs::getName(), new ListDatabaseLogs()); + $service->addAction(GetDatabaseUsage::getName(), new GetDatabaseUsage()); + $service->addAction(ListDatabaseUsage::getName(), new ListDatabaseUsage()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php new file mode 100644 index 0000000000..5c544a9d1f --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -0,0 +1,142 @@ +registerTableActions($service); + $this->registerColumnActions($service); + $this->registerColumnIndexActions($service); + $this->registerRowActions($service); + } + + private function registerTableActions(Service $service): void + { + $service->addAction(CreateTable::getName(), new CreateTable()); + $service->addAction(GetTable::getName(), new GetTable()); + $service->addAction(UpdateTable::getName(), new UpdateTable()); + $service->addAction(DeleteTable::getName(), new DeleteTable()); + $service->addAction(ListTables::getName(), new ListTables()); + $service->addAction(ListTableLogs::getName(), new ListTableLogs()); + $service->addAction(GetTableUsage::getName(), new GetTableUsage()); + } + + private function registerColumnActions(Service $service): void + { + // Column top level actions + $service->addAction(GetColumn::getName(), new GetColumn()); + $service->addAction(DeleteColumn::getName(), new DeleteColumn()); + $service->addAction(ListColumns::getName(), new ListColumns()); + + // Column: Boolean + $service->addAction(CreateBoolean::getName(), new CreateBoolean()); + $service->addAction(UpdateBoolean::getName(), new UpdateBoolean()); + + // Column: Datetime + $service->addAction(CreateDatetime::getName(), new CreateDatetime()); + $service->addAction(UpdateDatetime::getName(), new UpdateDatetime()); + + // Column: Email + $service->addAction(CreateEmail::getName(), new CreateEmail()); + $service->addAction(UpdateEmail::getName(), new UpdateEmail()); + + // Column: Enum + $service->addAction(CreateEnum::getName(), new CreateEnum()); + $service->addAction(UpdateEnum::getName(), new UpdateEnum()); + + // Column: Float + $service->addAction(CreateFloat::getName(), new CreateFloat()); + $service->addAction(UpdateFloat::getName(), new UpdateFloat()); + + // Column: Integer + $service->addAction(CreateInteger::getName(), new CreateInteger()); + $service->addAction(UpdateInteger::getName(), new UpdateInteger()); + + // Column: IP + $service->addAction(CreateIP::getName(), new CreateIP()); + $service->addAction(UpdateIP::getName(), new UpdateIP()); + + // Column: Relationship + $service->addAction(CreateRelationship::getName(), new CreateRelationship()); + $service->addAction(UpdateRelationship::getName(), new UpdateRelationship()); + + // Column: String + $service->addAction(CreateString::getName(), new CreateString()); + $service->addAction(UpdateString::getName(), new UpdateString()); + + // Column: URL + $service->addAction(CreateURL::getName(), new CreateURL()); + $service->addAction(UpdateURL::getName(), new UpdateURL()); + } + + private function registerColumnIndexActions(Service $service): void + { + $service->addAction(CreateColumnIndex::getName(), new CreateColumnIndex()); + $service->addAction(GetColumnIndex::getName(), new GetColumnIndex()); + $service->addAction(DeleteColumnIndex::getName(), new DeleteColumnIndex()); + $service->addAction(ListColumnIndexes::getName(), new ListColumnIndexes()); + } + + private function registerRowActions(Service $service): void + { + $service->addAction(CreateRow::getName(), new CreateRow()); + $service->addAction(GetRow::getName(), new GetRow()); + $service->addAction(UpdateRow::getName(), new UpdateRow()); + $service->addAction(DeleteRow::getName(), new DeleteRow()); + $service->addAction(ListRows::getName(), new ListRows()); + $service->addAction(ListRowLogs::getName(), new ListRowLogs()); + } +} From 44a5fef3455d84f98f2434dbd0a5c94474e0530a Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 19:17:06 +0530 Subject: [PATCH 073/173] misc: name fixes. --- .../Collections/Attributes/Boolean/Create.php | 2 +- .../Collections/Attributes/Boolean/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Get.php | 6 +++--- .../Http/Databases/Collections/Attributes/XList.php | 3 +-- .../Http/Databases/Collections/Documents/Create.php | 8 ++++---- .../Http/Databases/Collections/Documents/Get.php | 2 +- .../Http/Databases/Collections/Documents/Update.php | 6 +++--- .../Http/Databases/Collections/Documents/XList.php | 6 +++--- .../Http/Databases/Collections/Indexes/Create.php | 12 ++++++------ .../Http/Databases/Tables/Indexes/Create.php | 7 ++++++- .../Http/Databases/Tables/Indexes/Delete.php | 7 ++++++- .../Databases/Http/Databases/Tables/Indexes/Get.php | 7 ++++++- .../Http/Databases/Tables/Indexes/XList.php | 7 ++++++- .../Modules/Databases/Services/Registry/Tables.php | 4 ++-- 14 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php index 56c0753f08..7eb6a109c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php @@ -23,7 +23,7 @@ class Create extends Action public static function getName(): string { - return 'createBooleanColumn'; + return 'createBooleanAttribute'; } protected function getResponseModel(): string|array diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php index c8aa734570..9930ebdd8a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php @@ -23,7 +23,7 @@ class Update extends Action public static function getName(): string { - return 'updateBooleanColumn'; + return 'updateBooleanAttribute'; } protected function getResponseModel(): string|array diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index e04a89295b..3175efdd91 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -20,7 +20,7 @@ class Get extends Action public static function getName(): string { - return 'getColumn'; + return 'getAttribute'; } protected function getResponseModel(): string|array @@ -44,7 +44,7 @@ class Get extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') - ->desc('Get column') + ->desc('Get attribute') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) @@ -63,7 +63,7 @@ class Get extends Action )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') - ->param('key', '', new Key(), 'Column Key.') + ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') ->callback([$this, 'action']); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index 29e8d1d6b9..fb77626021 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -7,7 +7,6 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Attributes; -use Appwrite\Utopia\Database\Validator\Queries\Columns; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -57,7 +56,7 @@ class XList extends Action )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') - ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) + ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') ->callback([$this, 'action']); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 6db7e802f3..c66f205a8c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -179,7 +179,7 @@ class Create extends Action $relationships = \array_filter( $collection->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -261,7 +261,7 @@ class Create extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -300,10 +300,10 @@ class Create extends Action ->dynamic($document, $this->getResponseModel()); $relationships = \array_map( - fn ($row) => $document->getAttribute('key'), + fn ($document) => $document->getAttribute('key'), \array_filter( $collection->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index f56ab40126..aa01eebcdf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -113,7 +113,7 @@ class Get extends Action $relationships = \array_filter( $collection->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index be009b9cd2..9b373c7e17 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -159,7 +159,7 @@ class Update extends Action $relationships = \array_filter( $collection->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -256,7 +256,7 @@ class Update extends Action $relationships = \array_filter( $table->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); foreach ($relationships as $relationship) { @@ -290,7 +290,7 @@ class Update extends Action fn ($row) => $document->getAttribute('key'), \array_filter( $collection->getAttribute('attributes', []), - fn ($column) => $column->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ) ); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 7b4f5855e2..57af51d908 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -212,12 +212,12 @@ class XList extends Action } if ($select) { - foreach ($documents as $row) { + foreach ($documents as $document) { if (!$hasDatabaseId) { - $row->removeAttribute('$databaseId'); + $document->removeAttribute('$databaseId'); } if (!$hasCollectionId) { - $row->removeAttribute('$collectionId'); + $document->removeAttribute('$collectionId'); } } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index fe21025740..6370a32a4c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -150,23 +150,23 @@ class Create extends Action throw new Exception($this->getParentUnknownException(), "Unknown $contextType: " . $attribute . ". Verify the $contextType name or create the $contextType."); } - $columnStatus = $oldAttributes[$attributeIndex]['status']; - $columnType = $oldAttributes[$attributeIndex]['type']; - $columnArray = $oldAttributes[$attributeIndex]['array'] ?? false; + $attributeStatus = $oldAttributes[$attributeIndex]['status']; + $attributeType = $oldAttributes[$attributeIndex]['type']; + $attributeArray = $oldAttributes[$attributeIndex]['array'] ?? false; - if ($columnType === Database::VAR_RELATIONSHIP) { + if ($attributeType === Database::VAR_RELATIONSHIP) { throw new Exception($this->getParentInvalidTypeException(), "Cannot create an index for a relationship $contextType: " . $oldAttributes[$attributeIndex]['key']); } // ensure attribute is available - if ($columnStatus !== 'available') { + if ($attributeStatus !== 'available') { $contextType = ucfirst($contextType); throw new Exception($this->getParentNotAvailableException(), "$contextType not available: " . $oldAttributes[$attributeIndex]['key']); } $lengths[$i] = null; - if ($columnArray === true) { + if ($attributeArray === true) { $lengths[$i] = Database::ARRAY_INDEX_LENGTH; $orders[$i] = null; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index 97ac92d559..77e31f4115 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -22,6 +22,11 @@ class Create extends IndexCreate { use HTTP; + public static function getName(): string + { + return 'createColumnIndex'; + } + protected function getResponseModel(): string { return UtopiaResponse::MODEL_COLUMN_INDEX; @@ -44,7 +49,7 @@ class Create extends IndexCreate ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), - name: self::getName(), + name: 'createIndex', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/databases/create-index.md', auth: [AuthType::KEY], responses: [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index 6a86a4767e..7a81942e32 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -20,6 +20,11 @@ class Delete extends IndexDelete { use HTTP; + public static function getName(): string + { + return 'updateColumnIndex'; + } + /** * 1. `SDKResponse` uses `UtopiaResponse::MODEL_NONE`. * 2. But we later need the actual return type for events queue below! @@ -46,7 +51,7 @@ class Delete extends IndexDelete ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), - name: self::getName(), + name: 'deleteIndex', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/databases/delete-index.md', auth: [AuthType::KEY], responses: [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index 0cc537d960..a765bfb975 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -18,6 +18,11 @@ class Get extends IndexGet { use HTTP; + public static function getName(): string + { + return 'getColumnIndex'; + } + protected function getResponseModel(): string { return UtopiaResponse::MODEL_COLUMN_INDEX; @@ -37,7 +42,7 @@ class Get extends IndexGet ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), - name: self::getName(), + name: 'getIndex', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/databases/get-index.md', auth: [AuthType::KEY], responses: [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index 5a245cf533..afce04fcf9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -18,6 +18,11 @@ class XList extends IndexXList { use HTTP; + public static function getName(): string + { + return 'listColumnIndexes'; + } + protected function getResponseModel(): string { return UtopiaResponse::MODEL_COLUMN_INDEX_LIST; @@ -37,7 +42,7 @@ class XList extends IndexXList ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), - name: self::getName(), + name: 'listIndexes', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/databases/list-indexes.md', auth: [AuthType::KEY], responses: [ diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php index 5c544a9d1f..d881303c1e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -59,7 +59,7 @@ class Tables extends Base { $this->registerTableActions($service); $this->registerColumnActions($service); - $this->registerColumnIndexActions($service); + $this->registerIndexActions($service); $this->registerRowActions($service); } @@ -122,7 +122,7 @@ class Tables extends Base $service->addAction(UpdateURL::getName(), new UpdateURL()); } - private function registerColumnIndexActions(Service $service): void + private function registerIndexActions(Service $service): void { $service->addAction(CreateColumnIndex::getName(), new CreateColumnIndex()); $service->addAction(GetColumnIndex::getName(), new GetColumnIndex()); From 8006c9d60b62947e226963e37dceb3f491b004a3 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 19:36:59 +0530 Subject: [PATCH 074/173] misc: fixes. --- .../Collections/Attributes/Action.php | 24 +++++++++---------- .../Databases/Collections/Attributes/Get.php | 6 ++--- .../Collections/Attributes/XList.php | 4 ++-- .../Collections/Documents/Create.php | 12 +++++----- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index b683ebd64c..fcb9bcc2df 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -308,8 +308,8 @@ abstract class Action extends UtopiaAction if ($type === Database::VAR_RELATIONSHIP) { $options['side'] = Database::RELATION_SIDE_PARENT; - $relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); - if ($relatedTable->isEmpty()) { + $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); + if ($relatedCollection->isEmpty()) { $parent = $this->isCollectionsAPI() ? 'collection' : 'table'; throw new Exception($this->getParentNotFoundException(), "The related $parent was not found."); } @@ -359,12 +359,12 @@ abstract class Action extends UtopiaAction try { $twoWayAttribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey), + '$id' => ID::custom($db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $twoWayKey), 'key' => $twoWayKey, 'databaseInternalId' => $db->getInternalId(), 'databaseId' => $db->getId(), - 'collectionInternalId' => $relatedTable->getInternalId(), - 'collectionId' => $relatedTable->getId(), + 'collectionInternalId' => $relatedCollection->getInternalId(), + 'collectionId' => $relatedCollection->getId(), 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck 'size' => $size, @@ -378,7 +378,7 @@ abstract class Action extends UtopiaAction 'options' => $options, ]); - $dbForProject->checkAttribute($relatedTable, $twoWayAttribute); + $dbForProject->checkAttribute($relatedCollection, $twoWayAttribute); $dbForProject->createDocument('attributes', $twoWayAttribute); } catch (DuplicateException) { $dbForProject->deleteDocument('attributes', $attribute->getId()); @@ -387,13 +387,13 @@ abstract class Action extends UtopiaAction $dbForProject->deleteDocument('attributes', $attribute->getId()); throw new Exception($this->getLimitException()); } catch (Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); throw $e; } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); } $queueForDatabase @@ -447,11 +447,11 @@ abstract class Action extends UtopiaAction throw new Exception($this->getNotAvailableException()); } - if ($attribute->getAttribute('type') !== $type) { + if ($attribute->getAttribute(('type') !== $type)) { throw new Exception($this->getTypeInvalidException()); } - if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute('filter') !== $filter) { + if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) { throw new Exception($this->getTypeInvalidException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index 3175efdd91..96e40c750d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -62,21 +62,21 @@ class Get extends Action ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') ->callback([$this, 'action']); } - public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index fb77626021..7f2f23df62 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -62,14 +62,14 @@ class XList extends Action ->callback([$this, 'action']); } - public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId); + $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index c66f205a8c..297d0f3543 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -197,9 +197,9 @@ class Create extends Action $relations = [$related]; } - $relatedTableId = $relationship->getAttribute('relatedCollection'); - $relatedTable = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId) + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); foreach ($relations as &$relation) { @@ -213,7 +213,7 @@ class Create extends Action } if ($relation instanceof Document) { $current = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId()) + fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) ); if ($current->isEmpty()) { @@ -225,11 +225,11 @@ class Create extends Action } else { $relation->removeAttribute('$collectionId'); $relation->removeAttribute('$databaseId'); - $relation->setAttribute('$collection', $relatedTable->getId()); + $relation->setAttribute('$collection', $relatedCollection->getId()); $type = Database::PERMISSION_UPDATE; } - $checkPermissions($relatedTable, $relation, $type); + $checkPermissions($relatedCollection, $relation, $type); } } From 36e66170903e181b19e54c63f7fc93f5d8240650 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 19:47:35 +0530 Subject: [PATCH 075/173] update: the usage modals. --- app/controllers/api/project.php | 1 + .../Databases/Http/Databases/Usage/Get.php | 4 +++ .../Databases/Http/Databases/Usage/XList.php | 4 +++ .../Utopia/Response/Model/UsageDatabase.php | 23 ++++++++++++++-- .../Utopia/Response/Model/UsageDatabases.php | 27 ++++++++++++++++++- .../Utopia/Response/Model/UsageProject.php | 7 ++++- 6 files changed, 62 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 047179b888..079fabb622 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -354,6 +354,7 @@ App::get('/v1/project/usage') 'executionsMbSecondsTotal' => $total[METRIC_EXECUTIONS_MB_SECONDS], 'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS], 'documentsTotal' => $total[METRIC_DOCUMENTS], + 'rowsTotal' => $total[METRIC_DOCUMENTS], 'databasesTotal' => $total[METRIC_DATABASES], 'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE], 'usersTotal' => $total[METRIC_USERS], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index c4105effa0..222201d5ed 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -124,12 +124,16 @@ class Get extends Action $response->dynamic(new Document([ 'range' => $range, 'collectionsTotal' => $usage[$metrics[0]]['total'], + 'tablesTotal' => $usage[$metrics[0]]['total'], 'documentsTotal' => $usage[$metrics[1]]['total'], + 'rowsTotal' => $usage[$metrics[1]]['total'], 'storageTotal' => $usage[$metrics[2]]['total'], 'databaseReadsTotal' => $usage[$metrics[3]]['total'], 'databaseWritesTotal' => $usage[$metrics[4]]['total'], 'collections' => $usage[$metrics[0]]['data'], + 'tables' => $usage[$metrics[0]]['data'], 'documents' => $usage[$metrics[1]]['data'], + 'rows' => $usage[$metrics[1]]['data'], 'storage' => $usage[$metrics[2]]['data'], 'databaseReads' => $usage[$metrics[3]]['data'], 'databaseWrites' => $usage[$metrics[4]]['data'], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index 0078d54a4b..48533935b3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -117,13 +117,17 @@ class XList extends Action 'range' => $range, 'databasesTotal' => $usage[$metrics[0]]['total'], 'collectionsTotal' => $usage[$metrics[1]]['total'], + 'tablesTotal' => $usage[$metrics[1]]['total'], 'documentsTotal' => $usage[$metrics[2]]['total'], + 'rowsTotal' => $usage[$metrics[2]]['total'], 'storageTotal' => $usage[$metrics[3]]['total'], 'databasesReadsTotal' => $usage[$metrics[4]]['total'], 'databasesWritesTotal' => $usage[$metrics[5]]['total'], 'databases' => $usage[$metrics[0]]['data'], 'collections' => $usage[$metrics[1]]['data'], + 'tables' => $usage[$metrics[1]]['data'], 'documents' => $usage[$metrics[2]]['data'], + 'rows' => $usage[$metrics[2]]['data'], 'storage' => $usage[$metrics[3]]['data'], 'databasesReads' => $usage[$metrics[4]]['data'], 'databasesWrites' => $usage[$metrics[5]]['data'], diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index 3adabae4c1..c7dc6d15ce 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -5,7 +5,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -// TODO: check what do we use for - collectionsTotal, documentsTotal, collections, documents class UsageDatabase extends Model { public function __construct() @@ -23,12 +22,18 @@ class UsageDatabase extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('collectionsTotal', [ + ->addRule('documentsTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of documents.', 'default' => 0, 'example' => 0, ]) + ->addRule('rowsTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of rows.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('storageTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of total storage used in bytes.', @@ -54,6 +59,13 @@ class UsageDatabase extends Model 'example' => [], 'array' => true ]) + ->addRule('tables', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of tables per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('documents', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated number of documents per period.', @@ -61,6 +73,13 @@ class UsageDatabase extends Model 'example' => [], 'array' => true ]) + ->addRule('rows', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of rows per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('storage', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated storage used in bytes per period.', diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php index ae93182c0f..67568c858c 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php @@ -5,7 +5,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -// TODO: check what do we use for - collectionsTotal, documentsTotal, collections, documents class UsageDatabases extends Model { public function __construct() @@ -29,12 +28,24 @@ class UsageDatabases extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('tablesTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of tables.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('documentsTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of documents.', 'default' => 0, 'example' => 0, ]) + ->addRule('rowsTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of rows.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('storageTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of total databases storage in bytes.', @@ -67,6 +78,13 @@ class UsageDatabases extends Model 'example' => [], 'array' => true ]) + ->addRule('tables', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of tables per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('documents', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated number of documents per period.', @@ -74,6 +92,13 @@ class UsageDatabases extends Model 'example' => [], 'array' => true ]) + ->addRule('rows', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of rows per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('storage', [ 'type' => Response::MODEL_METRIC, 'description' => 'An array of the aggregated number of databases storage in bytes per period.', diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 70f8003cfd..ee644aa845 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -5,7 +5,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -// TODO: check what do we use for - documents. class UsageProject extends Model { public function __construct() @@ -23,6 +22,12 @@ class UsageProject extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('rowsTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of rows.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('databasesTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of databases.', From 5c0717458f00e126176787bcf0297074fbfa86b5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 19:51:16 +0530 Subject: [PATCH 076/173] update: the usage model. --- src/Appwrite/Utopia/Response/Model/UsageDatabase.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index c7dc6d15ce..990a2b3ee9 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -22,6 +22,12 @@ class UsageDatabase extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('tablesTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of tables.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('documentsTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of documents.', From c5074fd52fc9d132e706e59f22a5254c54962684 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 19:53:31 +0530 Subject: [PATCH 077/173] update: move around tests. --- .../{ => Collections}/DatabasesBase.php | 518 +++++++++--------- .../DatabasesConsoleClientTest.php | 20 +- .../DatabasesCustomClientTest.php | 70 +-- .../DatabasesCustomServerTest.php | 336 ++++++------ .../DatabasesPermissionsGuestTest.php | 24 +- .../DatabasesPermissionsMemberTest.php | 18 +- .../DatabasesPermissionsScope.php | 2 +- .../DatabasesPermissionsTeamTest.php | 14 +- 8 files changed, 500 insertions(+), 502 deletions(-) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesBase.php (93%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesConsoleClientTest.php (95%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesCustomClientTest.php (94%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesCustomServerTest.php (94%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesPermissionsGuestTest.php (95%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesPermissionsMemberTest.php (96%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesPermissionsScope.php (98%) rename tests/e2e/Services/Databases/{ => Collections}/DatabasesPermissionsTeamTest.php (95%) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/Collections/DatabasesBase.php similarity index 93% rename from tests/e2e/Services/Databases/DatabasesBase.php rename to tests/e2e/Services/Databases/Collections/DatabasesBase.php index 3c2a2c4186..9244e240f7 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesBase.php @@ -1,6 +1,6 @@ $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'documentSecurity' => true, 'permissions' => [ @@ -67,7 +66,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'documentSecurity' => true, 'permissions' => [ @@ -88,7 +87,7 @@ trait DatabasesBase /** * @depends testCreateCollection */ - public function testConsoleProject(array $data) + public function testConsoleProject(array $data): void { if ($this->getSide() === 'server') { // Server side can't get past the invalid key check anyway @@ -148,7 +147,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -275,7 +274,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $data['actorsId'], + 'relatedCollectionId' => $data['actorsId'], 'type' => 'oneToMany', 'twoWay' => true, 'key' => 'starringActors', @@ -339,7 +338,7 @@ trait DatabasesBase $this->assertEquals($relationship['headers']['status-code'], 202); $this->assertEquals($relationship['body']['key'], 'starringActors'); $this->assertEquals($relationship['body']['type'], 'relationship'); - $this->assertEquals($relationship['body']['relatedTable'], $data['actorsId']); + $this->assertEquals($relationship['body']['relatedCollection'], $data['actorsId']); $this->assertEquals($relationship['body']['relationType'], 'oneToMany'); $this->assertEquals($relationship['body']['twoWay'], true); $this->assertEquals($relationship['body']['twoWayKey'], 'movie'); @@ -360,17 +359,17 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertIsArray($movies['body']['columns']); - $this->assertCount(9, $movies['body']['columns']); - $this->assertEquals($movies['body']['columns'][0]['key'], $title['body']['key']); - $this->assertEquals($movies['body']['columns'][1]['key'], $description['body']['key']); - $this->assertEquals($movies['body']['columns'][2]['key'], $tagline['body']['key']); - $this->assertEquals($movies['body']['columns'][3]['key'], $releaseYear['body']['key']); - $this->assertEquals($movies['body']['columns'][4]['key'], $duration['body']['key']); - $this->assertEquals($movies['body']['columns'][5]['key'], $actors['body']['key']); - $this->assertEquals($movies['body']['columns'][6]['key'], $datetime['body']['key']); - $this->assertEquals($movies['body']['columns'][7]['key'], $relationship['body']['key']); - $this->assertEquals($movies['body']['columns'][8]['key'], $integers['body']['key']); + $this->assertIsArray($movies['body']['attributes']); + $this->assertCount(9, $movies['body']['attributes']); + $this->assertEquals($movies['body']['attributes'][0]['key'], $title['body']['key']); + $this->assertEquals($movies['body']['attributes'][1]['key'], $description['body']['key']); + $this->assertEquals($movies['body']['attributes'][2]['key'], $tagline['body']['key']); + $this->assertEquals($movies['body']['attributes'][3]['key'], $releaseYear['body']['key']); + $this->assertEquals($movies['body']['attributes'][4]['key'], $duration['body']['key']); + $this->assertEquals($movies['body']['attributes'][5]['key'], $actors['body']['key']); + $this->assertEquals($movies['body']['attributes'][6]['key'], $datetime['body']['key']); + $this->assertEquals($movies['body']['attributes'][7]['key'], $relationship['body']['key']); + $this->assertEquals($movies['body']['attributes'][8]['key'], $integers['body']['key']); return $data; } @@ -393,7 +392,7 @@ trait DatabasesBase ], ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(2, \count($response['body']['columns'])); + $this->assertEquals(2, \count($response['body']['attributes'])); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -417,7 +416,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'patch', 'documentSecurity' => true, 'permissions' => [ @@ -449,7 +448,7 @@ trait DatabasesBase ]), [ 'key' => 'titleIndex', 'type' => 'key', - 'columns' => ['title'], + 'attributes' => ['title'], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -488,7 +487,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Players', 'documentSecurity' => true, 'permissions' => [ @@ -540,7 +539,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Response Models', // 'permissions' missing on purpose to make sure it's optional 'documentSecurity' => true, @@ -654,7 +653,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $data['actorsId'], + 'relatedCollectionId' => $data['actorsId'], 'type' => 'oneToMany', 'twoWay' => true, 'key' => 'relationship', @@ -763,7 +762,7 @@ trait DatabasesBase $this->assertEquals('relationship', $relationship['body']['type']); $this->assertEquals(false, $relationship['body']['required']); $this->assertEquals(false, $relationship['body']['array']); - $this->assertEquals($data['actorsId'], $relationship['body']['relatedTable']); + $this->assertEquals($data['actorsId'], $relationship['body']['relatedCollection']); $this->assertEquals('oneToMany', $relationship['body']['relationType']); $this->assertEquals(true, $relationship['body']['twoWay']); $this->assertEquals('twoWayKey', $relationship['body']['twoWayKey']); @@ -947,7 +946,7 @@ trait DatabasesBase $this->assertEquals('available', $relationshipResponse['body']['status']); $this->assertEquals($relationship['body']['required'], $relationshipResponse['body']['required']); $this->assertEquals($relationship['body']['array'], $relationshipResponse['body']['array']); - $this->assertEquals($relationship['body']['relatedTable'], $relationshipResponse['body']['relatedTable']); + $this->assertEquals($relationship['body']['relatedCollection'], $relationshipResponse['body']['relatedCollection']); $this->assertEquals($relationship['body']['relationType'], $relationshipResponse['body']['relationType']); $this->assertEquals($relationship['body']['twoWay'], $relationshipResponse['body']['twoWay']); $this->assertEquals($relationship['body']['twoWayKey'], $relationshipResponse['body']['twoWayKey']); @@ -961,7 +960,7 @@ trait DatabasesBase $this->assertEquals(200, $attributes['headers']['status-code']); $this->assertEquals(12, $attributes['body']['total']); - $attributes = $attributes['body']['columns']; + $attributes = $attributes['body']['attributes']; $this->assertIsArray($attributes); $this->assertCount(12, $attributes); @@ -1043,7 +1042,7 @@ trait DatabasesBase $this->assertEquals($relationshipResponse['body']['status'], $attributes[9]['status']); $this->assertEquals($relationshipResponse['body']['required'], $attributes[9]['required']); $this->assertEquals($relationshipResponse['body']['array'], $attributes[9]['array']); - $this->assertEquals($relationshipResponse['body']['relatedTable'], $attributes[9]['relatedTable']); + $this->assertEquals($relationshipResponse['body']['relatedCollection'], $attributes[9]['relatedCollection']); $this->assertEquals($relationshipResponse['body']['relationType'], $attributes[9]['relationType']); $this->assertEquals($relationshipResponse['body']['twoWay'], $attributes[9]['twoWay']); $this->assertEquals($relationshipResponse['body']['twoWayKey'], $attributes[9]['twoWayKey']); @@ -1072,7 +1071,7 @@ trait DatabasesBase $this->assertEquals(200, $collection['headers']['status-code']); - $attributes = $collection['body']['columns']; + $attributes = $collection['body']['attributes']; $this->assertIsArray($attributes); $this->assertCount(12, $attributes); @@ -1155,7 +1154,7 @@ trait DatabasesBase $this->assertEquals($relationshipResponse['body']['status'], $attributes[9]['status']); $this->assertEquals($relationshipResponse['body']['required'], $attributes[9]['required']); $this->assertEquals($relationshipResponse['body']['array'], $attributes[9]['array']); - $this->assertEquals($relationshipResponse['body']['relatedTable'], $attributes[9]['relatedTable']); + $this->assertEquals($relationshipResponse['body']['relatedCollection'], $attributes[9]['relatedCollection']); $this->assertEquals($relationshipResponse['body']['relationType'], $attributes[9]['relationType']); $this->assertEquals($relationshipResponse['body']['twoWay'], $attributes[9]['twoWay']); $this->assertEquals($relationshipResponse['body']['twoWayKey'], $attributes[9]['twoWayKey']); @@ -1210,14 +1209,14 @@ trait DatabasesBase ]), [ 'key' => 'titleIndex', 'type' => 'fulltext', - 'columns' => ['title'], + 'attributes' => ['title'], ]); $this->assertEquals(202, $titleIndex['headers']['status-code']); $this->assertEquals('titleIndex', $titleIndex['body']['key']); $this->assertEquals('fulltext', $titleIndex['body']['type']); - $this->assertCount(1, $titleIndex['body']['columns']); - $this->assertEquals('title', $titleIndex['body']['columns'][0]); + $this->assertCount(1, $titleIndex['body']['attributes']); + $this->assertEquals('title', $titleIndex['body']['attributes'][0]); $releaseYearIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1226,14 +1225,14 @@ trait DatabasesBase ]), [ 'key' => 'releaseYear', 'type' => 'key', - 'columns' => ['releaseYear'], + 'attributes' => ['releaseYear'], ]); $this->assertEquals(202, $releaseYearIndex['headers']['status-code']); $this->assertEquals('releaseYear', $releaseYearIndex['body']['key']); $this->assertEquals('key', $releaseYearIndex['body']['type']); - $this->assertCount(1, $releaseYearIndex['body']['columns']); - $this->assertEquals('releaseYear', $releaseYearIndex['body']['columns'][0]); + $this->assertCount(1, $releaseYearIndex['body']['attributes']); + $this->assertEquals('releaseYear', $releaseYearIndex['body']['attributes'][0]); $releaseWithDate1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1242,16 +1241,16 @@ trait DatabasesBase ]), [ 'key' => 'releaseYearDated', 'type' => 'key', - 'columns' => ['releaseYear', '$createdAt', '$updatedAt'], + 'attributes' => ['releaseYear', '$createdAt', '$updatedAt'], ]); $this->assertEquals(202, $releaseWithDate1['headers']['status-code']); $this->assertEquals('releaseYearDated', $releaseWithDate1['body']['key']); $this->assertEquals('key', $releaseWithDate1['body']['type']); - $this->assertCount(3, $releaseWithDate1['body']['columns']); - $this->assertEquals('releaseYear', $releaseWithDate1['body']['columns'][0]); - $this->assertEquals('$createdAt', $releaseWithDate1['body']['columns'][1]); - $this->assertEquals('$updatedAt', $releaseWithDate1['body']['columns'][2]); + $this->assertCount(3, $releaseWithDate1['body']['attributes']); + $this->assertEquals('releaseYear', $releaseWithDate1['body']['attributes'][0]); + $this->assertEquals('$createdAt', $releaseWithDate1['body']['attributes'][1]); + $this->assertEquals('$updatedAt', $releaseWithDate1['body']['attributes'][2]); $releaseWithDate2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1260,14 +1259,14 @@ trait DatabasesBase ]), [ 'key' => 'birthDay', 'type' => 'key', - 'columns' => ['birthDay'], + 'attributes' => ['birthDay'], ]); $this->assertEquals(202, $releaseWithDate2['headers']['status-code']); $this->assertEquals('birthDay', $releaseWithDate2['body']['key']); $this->assertEquals('key', $releaseWithDate2['body']['type']); - $this->assertCount(1, $releaseWithDate2['body']['columns']); - $this->assertEquals('birthDay', $releaseWithDate2['body']['columns'][0]); + $this->assertCount(1, $releaseWithDate2['body']['attributes']); + $this->assertEquals('birthDay', $releaseWithDate2['body']['attributes'][0]); // Test for failure $fulltextReleaseYear = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ @@ -1277,7 +1276,7 @@ trait DatabasesBase ]), [ 'key' => 'releaseYearDated', 'type' => 'fulltext', - 'columns' => ['releaseYear'], + 'attributes' => ['releaseYear'], ]); $this->assertEquals(400, $fulltextReleaseYear['headers']['status-code']); @@ -1290,7 +1289,7 @@ trait DatabasesBase ]), [ 'key' => 'none', 'type' => 'key', - 'columns' => [], + 'attributes' => [], ]); $this->assertEquals(400, $noAttributes['headers']['status-code']); @@ -1303,7 +1302,7 @@ trait DatabasesBase ]), [ 'key' => 'duplicate', 'type' => 'fulltext', - 'columns' => ['releaseYear', 'releaseYear'], + 'attributes' => ['releaseYear', 'releaseYear'], ]); $this->assertEquals(400, $duplicates['headers']['status-code']); @@ -1316,7 +1315,7 @@ trait DatabasesBase ]), [ 'key' => 'tooLong', 'type' => 'key', - 'columns' => ['description', 'tagline'], + 'attributes' => ['description', 'tagline'], ]); $this->assertEquals(400, $tooLong['headers']['status-code']); @@ -1329,7 +1328,7 @@ trait DatabasesBase ]), [ 'key' => 'ft', 'type' => 'fulltext', - 'columns' => ['actors'], + 'attributes' => ['actors'], ]); $this->assertEquals(400, $fulltextArray['headers']['status-code']); @@ -1342,7 +1341,7 @@ trait DatabasesBase ]), [ 'key' => 'index-actors', 'type' => 'key', - 'columns' => ['actors'], + 'attributes' => ['actors'], ]); $this->assertEquals(202, $actorsArray['headers']['status-code']); @@ -1354,7 +1353,7 @@ trait DatabasesBase ]), [ 'key' => 'index-ip-actors', 'type' => 'key', - 'columns' => ['releaseYear', 'actors'], // 2 levels + 'attributes' => ['releaseYear', 'actors'], // 2 levels 'orders' => ['DESC', 'DESC'], ]); @@ -1369,11 +1368,11 @@ trait DatabasesBase ]), [ 'key' => 'index-unknown', 'type' => 'key', - 'columns' => ['Unknown'], + 'attributes' => ['Unknown'], ]); $this->assertEquals(400, $unknown['headers']['status-code']); - $this->assertEquals('Unknown column: Unknown. Verify the column name or create the column.', $unknown['body']['message']); + $this->assertEquals('Unknown attribute: Unknown. Verify the attribute name or create the attribute.', $unknown['body']['message']); $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -1382,7 +1381,7 @@ trait DatabasesBase ]), [ 'key' => 'integers-order', 'type' => 'key', - 'columns' => ['integers'], // array attribute + 'attributes' => ['integers'], // array attribute 'orders' => ['DESC'], // Check order is removed in API ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -1394,7 +1393,7 @@ trait DatabasesBase ]), [ 'key' => 'integers-size', 'type' => 'key', - 'columns' => ['integers'], // array attribute + 'attributes' => ['integers'], // array attribute ]); $this->assertEquals(202, $index2['headers']['status-code']); @@ -1463,7 +1462,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -1484,7 +1483,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Spider-Man: Far From Home', 'releaseYear' => 2019, @@ -1507,7 +1506,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Spider-Man: Homecoming', 'releaseYear' => 2017, @@ -1530,7 +1529,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'releaseYear' => 2020, // Missing title, expect an 400 error ], @@ -1542,7 +1541,7 @@ trait DatabasesBase ]); $this->assertEquals(201, $document1['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document1['body']['$tableId']); + $this->assertEquals($data['moviesId'], $document1['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $document1['body']); $this->assertEquals($databaseId, $document1['body']['$databaseId']); $this->assertEquals($document1['body']['title'], 'Captain America'); @@ -1555,7 +1554,7 @@ trait DatabasesBase $this->assertEquals($document1['body']['birthDay'], '1975-06-12T12:12:55.000+00:00'); $this->assertEquals(201, $document2['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document2['body']['$tableId']); + $this->assertEquals($data['moviesId'], $document2['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $document2['body']); $this->assertEquals($databaseId, $document2['body']['$databaseId']); $this->assertEquals($document2['body']['title'], 'Spider-Man: Far From Home'); @@ -1572,7 +1571,7 @@ trait DatabasesBase $this->assertEquals($document2['body']['integers'][1], 60); $this->assertEquals(201, $document3['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document3['body']['$tableId']); + $this->assertEquals($data['moviesId'], $document3['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $document3['body']); $this->assertEquals($databaseId, $document3['body']['$databaseId']); $this->assertEquals($document3['body']['title'], 'Spider-Man: Homecoming'); @@ -1612,17 +1611,16 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); - $this->assertEquals(2017, $documents['body']['rows'][1]['releaseYear']); - $this->assertEquals(2019, $documents['body']['rows'][2]['releaseYear']); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['rows'][0])); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['rows'][1])); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['rows'][2])); - $this->assertCount(3, $documents['body']['rows']); + $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); + $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $documents['body']['documents'][2]['releaseYear']); + $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][0])); + $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][1])); + $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][2])); + $this->assertCount(3, $documents['body']['documents']); - foreach ($documents['body']['rows'] as $document) { - print_r($document); - $this->assertEquals($data['moviesId'], $document['$tableId']); + foreach ($documents['body']['documents'] as $document) { + $this->assertEquals($data['moviesId'], $document['$collectionId']); $this->assertArrayNotHasKey('$collection', $document); $this->assertEquals($databaseId, $document['$databaseId']); } @@ -1637,10 +1635,10 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['rows'][2]['releaseYear']); - $this->assertEquals(2017, $documents['body']['rows'][1]['releaseYear']); - $this->assertEquals(2019, $documents['body']['rows'][0]['releaseYear']); - $this->assertCount(3, $documents['body']['rows']); + $this->assertEquals(1944, $documents['body']['documents'][2]['releaseYear']); + $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); + $this->assertCount(3, $documents['body']['documents']); // changing description attribute to be null by default instead of empty string $patchNull = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes/string/description', array_merge([ @@ -1656,7 +1654,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Dummy', 'releaseYear' => 1944, @@ -1686,7 +1684,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - return ['rows' => $documents['body']['rows'], 'databaseId' => $databaseId]; + return ['documents' => $documents['body']['documents'], 'databaseId' => $databaseId]; } @@ -1696,15 +1694,15 @@ trait DatabasesBase public function testGetDocument(array $data): void { $databaseId = $data['databaseId']; - foreach ($data['rows'] as $document) { - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$tableId'] . '/documents/' . $document['$id'], array_merge([ + foreach ($data['documents'] as $document) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($response['body']['$id'], $document['$id']); - $this->assertEquals($document['$tableId'], $response['body']['$tableId']); + $this->assertEquals($document['$collectionId'], $response['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $response['body']); $this->assertEquals($document['$databaseId'], $response['body']['$databaseId']); $this->assertEquals($response['body']['title'], $document['title']); @@ -1722,9 +1720,9 @@ trait DatabasesBase public function testGetDocumentWithQueries(array $data): void { $databaseId = $data['databaseId']; - $document = $data['rows'][0]; + $document = $data['documents'][0]; - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$tableId'] . '/documents/' . $document['$id'], array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1754,36 +1752,36 @@ trait DatabasesBase ], $this->getHeaders())); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals('Captain America', $base['body']['rows'][0]['title']); - $this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']); - $this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']); - $this->assertCount(3, $base['body']['rows']); + $this->assertEquals('Captain America', $base['body']['documents'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['documents'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['documents'][2]['title']); + $this->assertCount(3, $base['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString() + Query::cursorAfter(new Document(['$id' => $base['body']['documents'][0]['$id']]))->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['rows'][1]['$id'], $documents['body']['rows'][0]['$id']); - $this->assertEquals($base['body']['rows'][2]['$id'], $documents['body']['rows'][1]['$id']); - $this->assertCount(2, $documents['body']['rows']); + $this->assertEquals($base['body']['documents'][1]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][1]['$id']); + $this->assertCount(2, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString() + Query::cursorAfter(new Document(['$id' => $base['body']['documents'][2]['$id']]))->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEmpty($documents['body']['rows']); + $this->assertEmpty($documents['body']['documents']); /** * Test with ASC order and after. @@ -1798,24 +1796,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']); - $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']); - $this->assertCount(3, $base['body']['rows']); + $this->assertEquals(1944, $base['body']['documents'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['documents'][2]['releaseYear']); + $this->assertCount(3, $base['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), Query::orderAsc('releaseYear')->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['rows'][2]['$id'], $documents['body']['rows'][0]['$id']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertCount(1, $documents['body']['documents']); /** * Test with DESC order and after. @@ -1830,24 +1828,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']); - $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']); - $this->assertCount(3, $base['body']['rows']); + $this->assertEquals(1944, $base['body']['documents'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['documents'][0]['releaseYear']); + $this->assertCount(3, $base['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), Query::orderDesc('releaseYear')->toString() ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['rows'][2]['$id'], $documents['body']['rows'][0]['$id']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertCount(1, $documents['body']['documents']); /** * Test after with unknown document. @@ -1896,36 +1894,36 @@ trait DatabasesBase ], $this->getHeaders())); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals('Captain America', $base['body']['rows'][0]['title']); - $this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']); - $this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']); - $this->assertCount(3, $base['body']['rows']); + $this->assertEquals('Captain America', $base['body']['documents'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['documents'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['documents'][2]['title']); + $this->assertCount(3, $base['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['documents'][2]['$id']]))->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['rows'][0]['$id'], $documents['body']['rows'][0]['$id']); - $this->assertEquals($base['body']['rows'][1]['$id'], $documents['body']['rows'][1]['$id']); - $this->assertCount(2, $documents['body']['rows']); + $this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertEquals($base['body']['documents'][1]['$id'], $documents['body']['documents'][1]['$id']); + $this->assertCount(2, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['documents'][0]['$id']]))->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEmpty($documents['body']['rows']); + $this->assertEmpty($documents['body']['documents']); /** * Test with ASC order and after. @@ -1940,24 +1938,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']); - $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']); - $this->assertCount(3, $base['body']['rows']); + $this->assertEquals(1944, $base['body']['documents'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['documents'][2]['releaseYear']); + $this->assertCount(3, $base['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), Query::orderAsc('releaseYear')->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['rows'][0]['$id'], $documents['body']['rows'][0]['$id']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertCount(1, $documents['body']['documents']); /** * Test with DESC order and after. @@ -1972,24 +1970,24 @@ trait DatabasesBase ]); $this->assertEquals(200, $base['headers']['status-code']); - $this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']); - $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); - $this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']); - $this->assertCount(3, $base['body']['rows']); + $this->assertEquals(1944, $base['body']['documents'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['documents'][0]['releaseYear']); + $this->assertCount(3, $base['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(), Query::orderDesc('releaseYear')->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals($base['body']['rows'][0]['$id'], $documents['body']['rows'][0]['$id']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertCount(1, $documents['body']['documents']); return []; } @@ -2011,8 +2009,8 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2026,9 +2024,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2017, $documents['body']['rows'][0]['releaseYear']); - $this->assertEquals(2019, $documents['body']['rows'][1]['releaseYear']); - $this->assertCount(2, $documents['body']['rows']); + $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']); + $this->assertEquals(2019, $documents['body']['documents'][1]['releaseYear']); + $this->assertCount(2, $documents['body']['documents']); return []; } @@ -2049,21 +2047,21 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::equal('$id', [$documents['body']['rows'][0]['$id']])->toString(), + Query::equal('$id', [$documents['body']['documents'][0]['$id']])->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(1944, $documents['body']['rows'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2075,8 +2073,8 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2017, $documents['body']['rows'][0]['releaseYear']); - $this->assertCount(1, $documents['body']['rows']); + $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']); + $this->assertCount(1, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2088,9 +2086,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2019, $documents['body']['rows'][0]['releaseYear']); - $this->assertEquals(2017, $documents['body']['rows'][1]['releaseYear']); - $this->assertCount(2, $documents['body']['rows']); + $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); + $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); + $this->assertCount(2, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2125,8 +2123,8 @@ trait DatabasesBase ], ]); - $this->assertCount(1, $documents['body']['rows']); - $this->assertEquals('Captain America', $documents['body']['rows'][0]['title']); + $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals('Captain America', $documents['body']['documents'][0]['title']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2137,9 +2135,9 @@ trait DatabasesBase ], ]); - $this->assertCount(2, $documents['body']['rows']); - $this->assertEquals('Spider-Man: Far From Home', $documents['body']['rows'][0]['title']); - $this->assertEquals('Spider-Man: Homecoming', $documents['body']['rows'][1]['title']); + $this->assertCount(2, $documents['body']['documents']); + $this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['title']); + $this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['title']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2150,7 +2148,7 @@ trait DatabasesBase ], ]); - $this->assertCount(3, $documents['body']['rows']); + $this->assertCount(3, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2161,7 +2159,7 @@ trait DatabasesBase ], ]); - $this->assertCount(0, $documents['body']['rows']); + $this->assertCount(0, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2209,9 +2207,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals('1975-06-12T12:12:55.000+00:00', $documents['body']['rows'][0]['birthDay']); - $this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['rows'][1]['birthDay']); - $this->assertCount(2, $documents['body']['rows']); + $this->assertEquals('1975-06-12T12:12:55.000+00:00', $documents['body']['documents'][0]['birthDay']); + $this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['documents'][1]['birthDay']); + $this->assertCount(2, $documents['body']['documents']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2288,7 +2286,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Thor: Ragnaroc', 'releaseYear' => 2017, @@ -2306,7 +2304,7 @@ trait DatabasesBase $id = $document['body']['$id']; $this->assertEquals(201, $document['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document['body']['$tableId']); + $this->assertEquals($data['moviesId'], $document['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $document['body']); $this->assertEquals($databaseId, $document['body']['$databaseId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnaroc'); @@ -2334,7 +2332,7 @@ trait DatabasesBase $this->assertEquals(200, $document['headers']['status-code']); $this->assertEquals($document['body']['$id'], $id); - $this->assertEquals($data['moviesId'], $document['body']['$tableId']); + $this->assertEquals($data['moviesId'], $document['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $document['body']); $this->assertEquals($databaseId, $document['body']['$databaseId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok'); @@ -2351,7 +2349,7 @@ trait DatabasesBase $id = $document['body']['$id']; $this->assertEquals(200, $document['headers']['status-code']); - $this->assertEquals($data['moviesId'], $document['body']['$tableId']); + $this->assertEquals($data['moviesId'], $document['body']['$collectionId']); $this->assertArrayNotHasKey('$collection', $document['body']); $this->assertEquals($databaseId, $document['body']['$databaseId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok'); @@ -2399,7 +2397,7 @@ trait DatabasesBase $this->assertEquals(409, $response['headers']['status-code']); $this->assertEquals('Remote document is newer than local.', $response['body']['message']); - $this->assertEquals(Exception::ROW_UPDATE_CONFLICT, $response['body']['type']); + $this->assertEquals(Exception::DOCUMENT_UPDATE_CONFLICT, $response['body']['type']); return []; } @@ -2414,7 +2412,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Thor: Ragnarok', 'releaseYear' => 2017, @@ -2475,7 +2473,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'invalidDocumentStructure', 'permissions' => [ Permission::create(Role::any()), @@ -2672,7 +2670,7 @@ trait DatabasesBase $this->assertEquals(400, $enumDefault['headers']['status-code']); $this->assertEquals(400, $enumDefaultStrict['headers']['status-code']); $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']); - $this->assertEquals('Cannot set default value for array columns', $defaultArray['body']['message']); + $this->assertEquals('Cannot set default value for array attributes', $defaultArray['body']['message']); $this->assertEquals(400, $datetimeDefault['headers']['status-code']); // wait for worker to add attributes sleep(3); @@ -2683,7 +2681,7 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'], ]), []); - $this->assertCount(10, $collection['body']['columns']); + $this->assertCount(10, $collection['body']['attributes']); /** * Test for successful validation @@ -2693,7 +2691,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'email' => 'user@example.com', ], @@ -2708,7 +2706,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'enum' => 'yes', ], @@ -2723,7 +2721,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'ip' => '1.1.1.1', ], @@ -2738,7 +2736,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'url' => 'http://www.example.com', ], @@ -2753,7 +2751,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'range' => 3, ], @@ -2768,7 +2766,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'floatRange' => 1.4, ], @@ -2783,7 +2781,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'probability' => 0.99999, ], @@ -2798,7 +2796,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'upperBound' => 8, ], @@ -2813,7 +2811,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'lowerBound' => 8, ], @@ -2842,7 +2840,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'email' => 'user@@example.com', ], @@ -2857,7 +2855,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'enum' => 'badEnum', ], @@ -2872,7 +2870,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'ip' => '1.1.1.1.1', ], @@ -2887,7 +2885,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'url' => 'example...com', ], @@ -2902,7 +2900,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'range' => 11, ], @@ -2917,7 +2915,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'floatRange' => 2.5, ], @@ -2932,7 +2930,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'probability' => 1.1, ], @@ -2947,7 +2945,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'upperBound' => 11, ], @@ -2962,7 +2960,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'lowerBound' => 3, ], @@ -2977,7 +2975,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'birthDay' => '2020-10-10 27:30:10+01:00', ], @@ -3016,7 +3014,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -3146,7 +3144,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'enforceCollectionAndDocumentPermissions', 'documentSecurity' => true, 'permissions' => [ @@ -3188,7 +3186,7 @@ trait DatabasesBase ]), [ 'key' => 'key_attribute', 'type' => 'key', - 'columns' => [$attribute['body']['key']], + 'attributes' => [$attribute['body']['key']], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -3201,7 +3199,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3218,7 +3216,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3235,7 +3233,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3254,7 +3252,7 @@ trait DatabasesBase // Current user has read permission on the collection so can get any document $this->assertEquals(3, $documentsUser1['body']['total']); - $this->assertCount(3, $documentsUser1['body']['rows']); + $this->assertCount(3, $documentsUser1['body']['documents']); $document3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], array_merge([ 'content-type' => 'application/json', @@ -3316,7 +3314,7 @@ trait DatabasesBase // Current user has no collection permissions but has read permission for one document $this->assertEquals(1, $documentsUser2['body']['total']); - $this->assertCount(1, $documentsUser2['body']['rows']); + $this->assertCount(1, $documentsUser2['body']['documents']); } public function testEnforceCollectionPermissions() @@ -3339,7 +3337,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'enforceCollectionPermissions', 'permissions' => [ Permission::read(Role::user($user)), @@ -3377,7 +3375,7 @@ trait DatabasesBase ]), [ 'key' => 'key_attribute', 'type' => 'key', - 'columns' => [$attribute['body']['key']], + 'attributes' => [$attribute['body']['key']], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -3389,7 +3387,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3406,7 +3404,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3423,7 +3421,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'attribute' => 'one', ], @@ -3442,7 +3440,7 @@ trait DatabasesBase // Current user has read permission on the collection so can get any document $this->assertEquals(3, $documentsUser1['body']['total']); - $this->assertCount(3, $documentsUser1['body']['rows']); + $this->assertCount(3, $documentsUser1['body']['documents']); $document3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], array_merge([ 'content-type' => 'application/json', @@ -3514,7 +3512,7 @@ trait DatabasesBase // Current user has no collection permissions read access to one document $this->assertEquals(1, $documentsUser2['body']['total']); - $this->assertCount(1, $documentsUser2['body']['rows']); + $this->assertCount(1, $documentsUser2['body']['documents']); } /** @@ -3530,7 +3528,7 @@ trait DatabasesBase ]), [ 'key' => 'unique_title', 'type' => 'unique', - 'columns' => ['title'], + 'attributes' => ['title'], ]); $this->assertEquals(202, $uniqueIndex['headers']['status-code']); @@ -3542,7 +3540,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -3565,7 +3563,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America 5', 'releaseYear' => 1944, @@ -3588,7 +3586,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', 'releaseYear' => 1944, @@ -3624,7 +3622,7 @@ trait DatabasesBase ]; $document = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents', $headers, [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Creation Date Test', 'releaseYear' => 2000 @@ -3692,7 +3690,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::create(Role::user(ID::custom($this->getUser()['$id']))), @@ -3729,7 +3727,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -3803,7 +3801,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Boolean' ]); @@ -3848,7 +3846,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => 'person', + 'collectionId' => 'person', 'name' => 'person', 'permissions' => [ Permission::read(Role::user($this->getUser()['$id'])), @@ -3866,7 +3864,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => 'library', + 'collectionId' => 'library', 'name' => 'library', 'permissions' => [ Permission::read(Role::user($this->getUser()['$id'])), @@ -3895,7 +3893,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => 'library', + 'relatedCollectionId' => 'library', 'type' => Database::RELATION_ONE_TO_ONE, 'key' => 'library', 'twoWay' => true, @@ -3930,8 +3928,8 @@ trait DatabasesBase $this->assertEquals(200, $attributes['headers']['status-code']); $this->assertEquals(2, $attributes['body']['total']); - $attributes = $attributes['body']['columns']; - $this->assertEquals('library', $attributes[1]['relatedTable']); + $attributes = $attributes['body']['attributes']; + $this->assertEquals('library', $attributes[1]['relatedCollection']); $this->assertEquals('oneToOne', $attributes[1]['relationType']); $this->assertEquals(true, $attributes[1]['twoWay']); $this->assertEquals('person', $attributes[1]['twoWayKey']); @@ -3958,7 +3956,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'library' => [ '$id' => 'library1', @@ -3982,7 +3980,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'library' => [ 'libraryName' => 'Library 2', @@ -4001,8 +3999,8 @@ trait DatabasesBase $this->assertEquals($databaseId, $person1['body']['$databaseId']); $this->assertEquals($databaseId, $person1['body']['library']['$databaseId']); - $this->assertEquals($person['body']['$id'], $person1['body']['$tableId']); - $this->assertEquals($library['body']['$id'], $person1['body']['library']['$tableId']); + $this->assertEquals($person['body']['$id'], $person1['body']['$collectionId']); + $this->assertEquals($library['body']['$id'], $person1['body']['library']['$collectionId']); $this->assertArrayNotHasKey('$collection', $person1['body']); $this->assertArrayNotHasKey('$collection', $person1['body']['library']); @@ -4020,8 +4018,8 @@ trait DatabasesBase ]); $this->assertEquals(1, $documents['body']['total']); - $this->assertEquals('Library 1', $documents['body']['rows'][0]['library']['libraryName']); - $this->assertArrayHasKey('fullName', $documents['body']['rows'][0]); + $this->assertEquals('Library 1', $documents['body']['documents'][0]['library']['libraryName']); + $this->assertArrayHasKey('fullName', $documents['body']['documents'][0]); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4069,7 +4067,7 @@ trait DatabasesBase $this->assertEquals(200, $attributes['headers']['status-code']); $this->assertEquals(1, $attributes['body']['total']); - $this->assertEquals('libraryName', $attributes['body']['columns'][0]['key']); + $this->assertEquals('libraryName', $attributes['body']['attributes'][0]['key']); return [ 'databaseId' => $databaseId, @@ -4093,7 +4091,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => 'library', + 'relatedCollectionId' => 'library', 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'libraries', @@ -4108,9 +4106,9 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertIsArray($libraryAttributesResponse['body']['columns']); + $this->assertIsArray($libraryAttributesResponse['body']['attributes']); $this->assertEquals(2, $libraryAttributesResponse['body']['total']); - $this->assertEquals('person_one_to_many', $libraryAttributesResponse['body']['columns'][1]['key']); + $this->assertEquals('person_one_to_many', $libraryAttributesResponse['body']['attributes'][1]['key']); $libraryCollectionResponse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $libraryCollection, array_merge([ 'content-type' => 'application/json', @@ -4118,8 +4116,8 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertIsArray($libraryCollectionResponse['body']['columns']); - $this->assertCount(2, $libraryCollectionResponse['body']['columns']); + $this->assertIsArray($libraryCollectionResponse['body']['attributes']); + $this->assertCount(2, $libraryCollectionResponse['body']['attributes']); $attribute = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$personCollection}/attributes/libraries", array_merge([ 'content-type' => 'application/json', @@ -4142,7 +4140,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => 'person10', + 'documentId' => 'person10', 'data' => [ 'fullName' => 'Stevie Wonder', 'libraries' => [ @@ -4238,7 +4236,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Albums', 'documentSecurity' => true, 'permissions' => [ @@ -4264,7 +4262,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Artists', 'documentSecurity' => true, 'permissions' => [ @@ -4290,7 +4288,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $artists['body']['$id'], + 'relatedCollectionId' => $artists['body']['$id'], 'type' => Database::RELATION_MANY_TO_ONE, 'twoWay' => true, 'key' => 'artist', @@ -4319,7 +4317,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => 'album1', + 'documentId' => 'album1', 'permissions' => $permissions, 'data' => [ 'name' => 'Album 1', @@ -4382,7 +4380,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Sports', 'documentSecurity' => true, 'permissions' => [ @@ -4408,7 +4406,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Players', 'documentSecurity' => true, 'permissions' => [ @@ -4434,7 +4432,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $players['body']['$id'], + 'relatedCollectionId' => $players['body']['$id'], 'type' => Database::RELATION_MANY_TO_MANY, 'twoWay' => true, 'key' => 'players', @@ -4464,7 +4462,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => 'sport1', + 'documentId' => 'sport1', 'permissions' => $permissions, 'data' => [ 'name' => 'Sport 1', @@ -4542,10 +4540,10 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, count($response['body']['rows'])); - $this->assertEquals('person10', $response['body']['rows'][0]['$id']); - $this->assertEquals('Stevie Wonder', $response['body']['rows'][0]['fullName']); - $this->assertEquals(2, count($response['body']['rows'][0]['libraries'])); + $this->assertEquals(1, count($response['body']['documents'])); + $this->assertEquals('person10', $response['body']['documents'][0]['$id']); + $this->assertEquals('Stevie Wonder', $response['body']['documents'][0]['fullName']); + $this->assertEquals(2, count($response['body']['documents'][0]['libraries'])); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4559,11 +4557,11 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(2, count($response['body']['rows'])); - $this->assertEquals(null, $response['body']['rows'][0]['fullName']); - $this->assertArrayNotHasKey("libraries", $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); + $this->assertEquals(2, count($response['body']['documents'])); + $this->assertEquals(null, $response['body']['documents'][0]['fullName']); + $this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]); + $this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]); + $this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]); } /** @@ -4582,9 +4580,9 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertArrayNotHasKey('libraries', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('libraries', $response['body']['documents'][0]); + $this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]); + $this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4594,11 +4592,11 @@ trait DatabasesBase Query::select(['libraries.*', '$id'])->toString(), ], ]); - $document = $response['body']['rows'][0]; + $document = $response['body']['documents'][0]; $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('libraries', $document); $this->assertArrayNotHasKey('$databaseId', $document); - $this->assertArrayNotHasKey('$tableId', $document); + $this->assertArrayNotHasKey('$collectionId', $document); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', @@ -4642,7 +4640,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'USA Presidents', 'documentSecurity' => true, 'permissions' => [ @@ -4684,7 +4682,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'first_name' => 'Donald', 'last_name' => 'Trump', @@ -4699,7 +4697,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'first_name' => 'George', 'last_name' => 'Bush', @@ -4714,7 +4712,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'first_name' => 'Joe', 'last_name' => 'Biden', @@ -4747,7 +4745,7 @@ trait DatabasesBase ); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertCount(2, $documents['body']['rows']); + $this->assertCount(2, $documents['body']['documents']); } /** @@ -4765,7 +4763,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Collection1', 'documentSecurity' => true, 'permissions' => [ @@ -4779,7 +4777,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Collection2', 'documentSecurity' => true, 'permissions' => [ @@ -4816,7 +4814,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2, + 'relatedCollectionId' => $collection2, 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'collection2' @@ -4828,7 +4826,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Document 1', 'collection2' => [ @@ -4861,7 +4859,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Slow Queries', 'documentSecurity' => true, 'permissions' => [ @@ -4894,9 +4892,9 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ - 'longtext' => file_get_contents(__DIR__ . '/../../../resources/longtext.txt'), + 'longtext' => file_get_contents(__DIR__ . '../../../../../resources/longtext.txt'), ], 'permissions' => [ Permission::read(Role::user($this->getUser()['$id'])), diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/Collections/DatabasesConsoleClientTest.php similarity index 95% rename from tests/e2e/Services/Databases/DatabasesConsoleClientTest.php rename to tests/e2e/Services/Databases/Collections/DatabasesConsoleClientTest.php index e9dca1d674..64cade4f9d 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesConsoleClientTest.php @@ -1,6 +1,6 @@ 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -69,7 +69,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'TvShows', 'permissions' => [ Permission::read(Role::any()), @@ -224,12 +224,12 @@ class DatabasesConsoleClientTest extends Scope ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(11, count($response['body'])); + $this->assertEquals(15, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); - $this->assertIsNumeric($response['body']['rowsTotal']); - $this->assertIsNumeric($response['body']['tablesTotal']); - $this->assertIsArray($response['body']['tables']); - $this->assertIsArray($response['body']['rows']); + $this->assertIsNumeric($response['body']['documentsTotal']); + $this->assertIsNumeric($response['body']['collectionsTotal']); + $this->assertIsArray($response['body']['collections']); + $this->assertIsArray($response['body']['documents']); } @@ -273,8 +273,8 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(3, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); - $this->assertIsNumeric($response['body']['rowsTotal']); - $this->assertIsArray($response['body']['rows']); + $this->assertIsNumeric($response['body']['documentsTotal']); + $this->assertIsArray($response['body']['documents']); } /** diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/Collections/DatabasesCustomClientTest.php similarity index 94% rename from tests/e2e/Services/Databases/DatabasesCustomClientTest.php rename to tests/e2e/Services/Databases/Collections/DatabasesCustomClientTest.php index b192cd04a1..49839f83e1 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesCustomClientTest.php @@ -1,6 +1,6 @@ $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'documentSecurity' => true, 'permissions' => [ @@ -73,7 +73,7 @@ class DatabasesCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -95,7 +95,7 @@ class DatabasesCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Captain America', ], @@ -138,7 +138,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('permissionCheck'), + 'collectionId' => ID::custom('permissionCheck'), 'name' => 'permissionCheck', 'permissions' => [], 'documentSecurity' => true, @@ -166,7 +166,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => ID::custom('permissionCheckDocument'), + 'documentId' => ID::custom('permissionCheckDocument'), 'data' => [ 'name' => 'AppwriteBeginner', ], @@ -247,7 +247,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'level1', 'documentSecurity' => false, 'permissions' => [ @@ -264,7 +264,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'level2', 'documentSecurity' => false, 'permissions' => [ @@ -283,7 +283,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => 'oneToMany', 'twoWay' => true, 'onDelete' => 'cascade', @@ -309,11 +309,11 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); $this->assertEquals('restrict', $collection1RelationAttribute['onDelete']); } @@ -335,7 +335,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'c1', 'documentSecurity' => false, 'permissions' => [ @@ -351,7 +351,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'c2', 'documentSecurity' => false, 'permissions' => [ @@ -369,7 +369,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_ONE, 'twoWay' => false, 'onDelete' => 'cascade', @@ -387,7 +387,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => false, 'onDelete' => 'cascade', @@ -406,7 +406,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => false, 'onDelete' => 'cascade', @@ -424,7 +424,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => false, 'onDelete' => 'cascade', @@ -442,7 +442,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => Database::RELATION_MANY_TO_MANY, 'twoWay' => true, 'onDelete' => 'setNull', @@ -461,7 +461,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => Database::RELATION_MANY_TO_MANY, 'twoWay' => true, 'onDelete' => 'setNull', @@ -472,7 +472,7 @@ class DatabasesCustomClientTest extends Scope \sleep(2); $this->assertEquals(409, $relation['body']['code']); - $this->assertEquals('Creating more than one "manyToMany" relationship on the same table is currently not permitted.', $relation['body']['message']); + $this->assertEquals('Creating more than one "manyToMany" relationship on the same collection is currently not permitted.', $relation['body']['message']); } public function testUpdateWithoutRelationPermission(): void @@ -495,7 +495,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection1'), + 'collectionId' => ID::custom('collection1'), 'name' => ID::custom('collection1'), 'documentSecurity' => false, 'permissions' => [ @@ -511,7 +511,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection2'), + 'collectionId' => ID::custom('collection2'), 'name' => ID::custom('collection2'), 'documentSecurity' => false, 'permissions' => [ @@ -524,7 +524,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection3'), + 'collectionId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), 'documentSecurity' => false, 'permissions' => [ @@ -539,7 +539,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection4'), + 'collectionId' => ID::custom('collection4'), 'name' => ID::custom('collection4'), 'documentSecurity' => false, 'permissions' => [ @@ -552,7 +552,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection5'), + 'collectionId' => ID::custom('collection5'), 'name' => ID::custom('collection5'), 'documentSecurity' => false, 'permissions' => [ @@ -568,7 +568,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedCollectionId' => $collection2['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -581,7 +581,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection3['body']['$id'], + 'relatedCollectionId' => $collection3['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -594,7 +594,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection4['body']['$id'], + 'relatedCollectionId' => $collection4['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -607,7 +607,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection5['body']['$id'], + 'relatedCollectionId' => $collection5['body']['$id'], 'type' => 'oneToOne', 'twoWay' => false, 'onDelete' => 'setNull', @@ -681,7 +681,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => ID::custom($collection1['body']['$id']), + 'documentId' => ID::custom($collection1['body']['$id']), 'data' => [ 'Title' => 'Captain America', $collection2['body']['$id'] => [ @@ -709,7 +709,7 @@ class DatabasesCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::custom($collection1['body']['$id']), + 'documentId' => ID::custom($collection1['body']['$id']), 'data' => [ 'Title' => 'Captain America', $collection2['body']['$id'] => [ @@ -739,7 +739,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection3'), + 'collectionId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), 'documentSecurity' => false, 'permissions' => [ @@ -814,7 +814,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection3'), + 'collectionId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), 'documentSecurity' => false, 'permissions' => [ @@ -830,7 +830,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('collection2'), + 'collectionId' => ID::custom('collection2'), 'name' => ID::custom('collection2'), 'documentSecurity' => false, 'permissions' => [ @@ -847,7 +847,7 @@ class DatabasesCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => ID::custom('collection3Doc1'), + 'documentId' => ID::custom('collection3Doc1'), 'data' => [ 'Rating' => '20' ] diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php similarity index 94% rename from tests/e2e/Services/Databases/DatabasesCustomServerTest.php rename to tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php index 874aa5623e..e420681a2e 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php @@ -1,6 +1,6 @@ $this->getProject()['apiKey'] ]), [ 'name' => 'Test 1', - 'tableId' => ID::custom('first'), + 'collectionId' => ID::custom('first'), 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -374,7 +374,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test 2', - 'tableId' => ID::custom('second'), + 'collectionId' => ID::custom('second'), 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -390,12 +390,12 @@ class DatabasesCustomServerTest extends Scope ], $this->getHeaders())); $this->assertEquals(2, $collections['body']['total']); - $this->assertEquals($test1['body']['$id'], $collections['body']['tables'][0]['$id']); - $this->assertEquals($test1['body']['enabled'], $collections['body']['tables'][0]['enabled']); - $this->assertEquals($test2['body']['$id'], $collections['body']['tables'][1]['$id']); - $this->assertEquals($test1['body']['enabled'], $collections['body']['tables'][0]['enabled']); + $this->assertEquals($test1['body']['$id'], $collections['body']['collections'][0]['$id']); + $this->assertEquals($test1['body']['enabled'], $collections['body']['collections'][0]['enabled']); + $this->assertEquals($test2['body']['$id'], $collections['body']['collections'][1]['$id']); + $this->assertEquals($test1['body']['enabled'], $collections['body']['collections'][0]['enabled']); - $base = array_reverse($collections['body']['tables']); + $base = array_reverse($collections['body']['collections']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -407,7 +407,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(1, $collections['body']['tables']); + $this->assertCount(1, $collections['body']['collections']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -419,7 +419,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(1, $collections['body']['tables']); + $this->assertCount(1, $collections['body']['collections']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -431,7 +431,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(2, $collections['body']['tables']); + $this->assertCount(2, $collections['body']['collections']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -443,7 +443,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collections['headers']['status-code']); - $this->assertCount(0, $collections['body']['tables']); + $this->assertCount(0, $collections['body']['collections']); /** * Test for Order @@ -458,8 +458,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(2, $collections['body']['total']); - $this->assertEquals($base[0]['$id'], $collections['body']['tables'][0]['$id']); - $this->assertEquals($base[1]['$id'], $collections['body']['tables'][1]['$id']); + $this->assertEquals($base[0]['$id'], $collections['body']['collections'][0]['$id']); + $this->assertEquals($base[1]['$id'], $collections['body']['collections'][1]['$id']); /** * Test for After @@ -474,24 +474,24 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['tables'][0]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['collections'][0]['$id']]))->toString(), ], ]); - $this->assertCount(1, $collections['body']['tables']); - $this->assertEquals($base['body']['tables'][1]['$id'], $collections['body']['tables'][0]['$id']); + $this->assertCount(1, $collections['body']['collections']); + $this->assertEquals($base['body']['collections'][1]['$id'], $collections['body']['collections'][0]['$id']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['tables'][1]['$id']]))->toString(), + Query::cursorAfter(new Document(['$id' => $base['body']['collections'][1]['$id']]))->toString(), ], ]); - $this->assertCount(0, $collections['body']['tables']); - $this->assertEmpty($collections['body']['tables']); + $this->assertCount(0, $collections['body']['collections']); + $this->assertEmpty($collections['body']['collections']); /** * Test for Before @@ -506,24 +506,24 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['tables'][1]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['collections'][1]['$id']]))->toString(), ], ]); - $this->assertCount(1, $collections['body']['tables']); - $this->assertEquals($base['body']['tables'][0]['$id'], $collections['body']['tables'][0]['$id']); + $this->assertCount(1, $collections['body']['collections']); + $this->assertEquals($base['body']['collections'][0]['$id'], $collections['body']['collections'][0]['$id']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['tables'][0]['$id']]))->toString(), + Query::cursorBefore(new Document(['$id' => $base['body']['collections'][0]['$id']]))->toString(), ], ]); - $this->assertCount(0, $collections['body']['tables']); - $this->assertEmpty($collections['body']['tables']); + $this->assertCount(0, $collections['body']['collections']); + $this->assertEmpty($collections['body']['collections']); /** * Test for Search @@ -536,7 +536,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(1, $collections['body']['total']); - $this->assertEquals('first', $collections['body']['tables'][0]['$id']); + $this->assertEquals('first', $collections['body']['collections'][0]['$id']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -546,8 +546,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(2, $collections['body']['total']); - $this->assertEquals('Test 1', $collections['body']['tables'][0]['name']); - $this->assertEquals('Test 2', $collections['body']['tables'][1]['name']); + $this->assertEquals('Test 1', $collections['body']['collections'][0]['name']); + $this->assertEquals('Test 2', $collections['body']['collections'][1]['name']); $collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([ 'content-type' => 'application/json', @@ -579,7 +579,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test 1', - 'tableId' => ID::custom('first'), + 'collectionId' => ID::custom('first'), 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -592,7 +592,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(409, $response['headers']['status-code']); return [ 'databaseId' => $databaseId, - 'tableId' => $test1['body']['$id'], + 'collectionId' => $test1['body']['$id'], ]; } @@ -602,7 +602,7 @@ class DatabasesCustomServerTest extends Scope public function testGetCollection(array $data): void { $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -622,7 +622,7 @@ class DatabasesCustomServerTest extends Scope public function testUpdateCollection(array $data) { $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $collection = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -657,7 +657,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Encrypted Actors Data', 'permissions' => [ Permission::read(Role::any()), @@ -719,7 +719,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'firstName' => 'Jonah', 'lastName' => 'Jameson', @@ -767,7 +767,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -820,7 +820,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'firstName' => 'lorem', 'lastName' => 'ipsum', @@ -840,7 +840,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'key_lastName', 'type' => 'key', - 'columns' => [ + 'attributes' => [ 'lastName', ], ]); @@ -857,11 +857,11 @@ class DatabasesCustomServerTest extends Scope $unneededId = $unneeded['body']['key']; $this->assertEquals(200, $collection['headers']['status-code']); - $this->assertIsArray($collection['body']['columns']); - $this->assertCount(3, $collection['body']['columns']); - $this->assertEquals($collection['body']['columns'][0]['key'], $firstName['body']['key']); - $this->assertEquals($collection['body']['columns'][1]['key'], $lastName['body']['key']); - $this->assertEquals($collection['body']['columns'][2]['key'], $unneeded['body']['key']); + $this->assertIsArray($collection['body']['attributes']); + $this->assertCount(3, $collection['body']['attributes']); + $this->assertEquals($collection['body']['attributes'][0]['key'], $firstName['body']['key']); + $this->assertEquals($collection['body']['attributes'][1]['key'], $lastName['body']['key']); + $this->assertEquals($collection['body']['attributes'][2]['key'], $unneeded['body']['key']); $this->assertCount(1, $collection['body']['indexes']); $this->assertEquals($collection['body']['indexes'][0]['key'], $index['body']['key']); @@ -892,13 +892,13 @@ class DatabasesCustomServerTest extends Scope ]), []); $this->assertEquals(200, $collection['headers']['status-code']); - $this->assertIsArray($collection['body']['columns']); - $this->assertCount(2, $collection['body']['columns']); - $this->assertEquals($collection['body']['columns'][0]['key'], $firstName['body']['key']); - $this->assertEquals($collection['body']['columns'][1]['key'], $lastName['body']['key']); + $this->assertIsArray($collection['body']['attributes']); + $this->assertCount(2, $collection['body']['attributes']); + $this->assertEquals($collection['body']['attributes'][0]['key'], $firstName['body']['key']); + $this->assertEquals($collection['body']['attributes'][1]['key'], $lastName['body']['key']); return [ - 'tableId' => $actors['body']['$id'], + 'collectionId' => $actors['body']['$id'], 'key' => $index['body']['key'], 'databaseId' => $databaseId ]; @@ -910,7 +910,7 @@ class DatabasesCustomServerTest extends Scope public function testDeleteIndex($data): array { $databaseId = $data['databaseId']; - $index = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/indexes/' . $data['key'], array_merge([ + $index = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/indexes/' . $data['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -921,7 +921,7 @@ class DatabasesCustomServerTest extends Scope // Wait for database worker to finish deleting index sleep(2); - $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['tableId'], array_merge([ + $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['collectionId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -938,7 +938,7 @@ class DatabasesCustomServerTest extends Scope public function testDeleteIndexOnDeleteAttribute($data) { $databaseId = $data['databaseId']; - $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/string', array_merge([ + $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -948,7 +948,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/string', array_merge([ + $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -965,25 +965,25 @@ class DatabasesCustomServerTest extends Scope sleep(2); - $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/indexes', array_merge([ + $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'key' => 'index1', 'type' => 'key', - 'columns' => ['attribute1', 'attribute2'], + 'attributes' => ['attribute1', 'attribute2'], 'orders' => ['ASC', 'ASC'], ]); - $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/indexes', array_merge([ + $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'key' => 'index2', 'type' => 'key', - 'columns' => ['attribute2'], + 'attributes' => ['attribute2'], ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -994,7 +994,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); // Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/' . $attribute2['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/' . $attribute2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1005,7 +1005,7 @@ class DatabasesCustomServerTest extends Scope // wait for database worker to complete sleep(2); - $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['tableId'], array_merge([ + $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['collectionId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1015,12 +1015,12 @@ class DatabasesCustomServerTest extends Scope $this->assertIsArray($collection['body']['indexes']); $this->assertCount(1, $collection['body']['indexes']); $this->assertEquals($index1['body']['key'], $collection['body']['indexes'][0]['key']); - $this->assertIsArray($collection['body']['indexes'][0]['columns']); - $this->assertCount(1, $collection['body']['indexes'][0]['columns']); - $this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['columns'][0]); + $this->assertIsArray($collection['body']['indexes'][0]['attributes']); + $this->assertCount(1, $collection['body']['indexes'][0]['attributes']); + $this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); // Delete attribute - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['tableId'] . '/attributes/' . $attribute1['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['collectionId'] . '/attributes/' . $attribute1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1050,7 +1050,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute', 'permissions' => [ Permission::read(Role::any()), @@ -1100,7 +1100,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index1', 'type' => 'key', - 'columns' => ['attribute1', 'attribute2'], + 'attributes' => ['attribute1', 'attribute2'], 'orders' => ['ASC', 'ASC'], ]); @@ -1111,7 +1111,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index2', 'type' => 'key', - 'columns' => ['attribute2'], + 'attributes' => ['attribute2'], ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -1143,9 +1143,9 @@ class DatabasesCustomServerTest extends Scope $this->assertIsArray($collection['body']['indexes']); $this->assertCount(1, $collection['body']['indexes']); $this->assertEquals($index2['body']['key'], $collection['body']['indexes'][0]['key']); - $this->assertIsArray($collection['body']['indexes'][0]['columns']); - $this->assertCount(1, $collection['body']['indexes'][0]['columns']); - $this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['columns'][0]); + $this->assertIsArray($collection['body']['indexes'][0]['attributes']); + $this->assertCount(1, $collection['body']['indexes'][0]['attributes']); + $this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); // Delete attribute $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/' . $attribute2['body']['key'], array_merge([ @@ -1163,14 +1163,14 @@ class DatabasesCustomServerTest extends Scope public function testDeleteCollection($data) { $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; // Add Documents to the collection $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'firstName' => 'Tom', 'lastName' => 'Holland', @@ -1186,7 +1186,7 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'firstName' => 'Samuel', 'lastName' => 'Jackson', @@ -1249,7 +1249,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Collection1', 'documentSecurity' => false, 'permissions' => [], @@ -1260,7 +1260,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Collection2', 'documentSecurity' => false, 'permissions' => [], @@ -1274,7 +1274,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]), [ - 'relatedTableId' => $collection2, + 'relatedCollectionId' => $collection2, 'type' => Database::RELATION_MANY_TO_ONE, 'twoWay' => false, 'key' => 'collection2' @@ -1318,7 +1318,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('attributeRowWidthLimit'), + 'collectionId' => ID::custom('attributeRowWidthLimit'), 'name' => 'attributeRowWidthLimit', 'permissions' => [ Permission::read(Role::any()), @@ -1384,7 +1384,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('testLimitException'), + 'collectionId' => ID::custom('testLimitException'), 'name' => 'testLimitException', 'permissions' => [ Permission::read(Role::any()), @@ -1426,12 +1426,12 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $collection['headers']['status-code']); $this->assertEquals($collection['body']['name'], 'testLimitException'); - $this->assertIsArray($collection['body']['columns']); + $this->assertIsArray($collection['body']['attributes']); $this->assertIsArray($collection['body']['indexes']); - $this->assertCount(64, $collection['body']['columns']); + $this->assertCount(64, $collection['body']['attributes']); $this->assertCount(0, $collection['body']['indexes']); - foreach ($collection['body']['columns'] as $attribute) { + foreach ($collection['body']['attributes'] as $attribute) { $this->assertEquals('available', $attribute['status'], 'attribute: ' . $attribute['key']); } @@ -1446,7 +1446,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => "key_attribute{$i}", 'type' => 'key', - 'columns' => ["attribute{$i}"], + 'attributes' => ["attribute{$i}"], ]); $this->assertEquals(202, $index['headers']['status-code']); @@ -1463,9 +1463,9 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $collection['headers']['status-code']); $this->assertEquals($collection['body']['name'], 'testLimitException'); - $this->assertIsArray($collection['body']['columns']); + $this->assertIsArray($collection['body']['attributes']); $this->assertIsArray($collection['body']['indexes']); - $this->assertCount(64, $collection['body']['columns']); + $this->assertCount(64, $collection['body']['attributes']); $this->assertCount(58, $collection['body']['indexes']); $tooMany = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/indexes', array_merge([ @@ -1475,7 +1475,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'tooMany', 'type' => 'key', - 'columns' => ['attribute61'], + 'attributes' => ['attribute61'], ]); $this->assertEquals(400, $tooMany['headers']['status-code']); @@ -1508,7 +1508,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('updateAttributes'), + 'collectionId' => ID::custom('updateAttributes'), 'name' => 'updateAttributes' ]); @@ -1642,7 +1642,7 @@ class DatabasesCustomServerTest extends Scope return [ 'databaseId' => $databaseId, - 'tableId' => $collectionId + 'collectionId' => $collectionId ]; } @@ -1653,7 +1653,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'string'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string/' . $key, array_merge([ 'content-type' => 'application/json', @@ -1681,7 +1681,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('lorem', $attribute['default']); @@ -1785,7 +1785,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -1795,7 +1795,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'email'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/email/' . $key, array_merge([ 'content-type' => 'application/json', @@ -1823,7 +1823,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('torsten@appwrite.io', $attribute['default']); @@ -1928,7 +1928,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -1938,7 +1938,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'ip'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/ip/' . $key, array_merge([ 'content-type' => 'application/json', @@ -1966,7 +1966,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('127.0.0.1', $attribute['default']); @@ -2070,7 +2070,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -2080,7 +2080,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'url'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/url/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2108,7 +2108,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('http://appwrite.io', $attribute['default']); @@ -2212,7 +2212,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -2222,7 +2222,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'integer'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2254,7 +2254,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals(123, $attribute['default']); @@ -2430,7 +2430,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2444,7 +2444,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2458,7 +2458,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ @@ -2473,7 +2473,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); } /** @@ -2483,7 +2483,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'float'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2515,7 +2515,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals(123.456, $attribute['default']); @@ -2691,7 +2691,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2705,7 +2705,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2719,7 +2719,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ @@ -2734,7 +2734,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); } /** @@ -2744,7 +2744,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'boolean'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/boolean/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2772,7 +2772,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals(true, $attribute['default']); @@ -2876,7 +2876,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -2886,7 +2886,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'datetime'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/datetime/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2914,7 +2914,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('1975-06-12 14:12:55+02:00', $attribute['default']); @@ -3018,7 +3018,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -3028,7 +3028,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'enum'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3061,7 +3061,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $attribute = array_values(array_filter($new['body']['attributes'], fn (array $a) => $a['key'] === $key))[0] ?? null; $this->assertNotNull($attribute); $this->assertFalse($attribute['required']); $this->assertEquals('lorem', $attribute['default']); @@ -3133,7 +3133,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3146,7 +3146,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_VALUE_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3234,7 +3234,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_DEFAULT_UNSUPPORTED, $update['body']['type']); } /** @@ -3244,7 +3244,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'string'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $document = $this->client->call( Client::METHOD_POST, @@ -3255,7 +3255,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'string' => 'string' ], @@ -3287,7 +3287,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'string' => str_repeat('a', 2048) ], @@ -3324,7 +3324,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $attribute['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_INVALID_RESIZE, $attribute['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_INVALID_RESIZE, $attribute['body']['type']); // original documents to original size, remove new document $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document['body']['$id'], array_merge([ @@ -3373,7 +3373,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'string' => str_repeat('a', 10) ], @@ -3408,7 +3408,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'string' => str_repeat('a', 11) ], @@ -3417,7 +3417,7 @@ class DatabasesCustomServerTest extends Scope ); $this->assertEquals(400, $newDoc['headers']['status-code']); - $this->assertEquals(AppwriteException::ROW_INVALID_STRUCTURE, $newDoc['body']['type']); + $this->assertEquals(AppwriteException::DOCUMENT_INVALID_STRUCTURE, $newDoc['body']['type']); } /** @@ -3426,7 +3426,7 @@ class DatabasesCustomServerTest extends Scope public function testAttributeUpdateNotFound(array $data) { $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $attributes = [ 'string' => [ @@ -3491,7 +3491,7 @@ class DatabasesCustomServerTest extends Scope ]), $payload); $this->assertEquals(404, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::TABLE_NOT_FOUND, $update['body']['type']); + $this->assertEquals(AppwriteException::COLLECTION_NOT_FOUND, $update['body']['type']); /** * Check if Attribute exists @@ -3503,7 +3503,7 @@ class DatabasesCustomServerTest extends Scope ]), $payload); $this->assertEquals(404, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_NOT_FOUND, $update['body']['type']); + $this->assertEquals(AppwriteException::ATTRIBUTE_NOT_FOUND, $update['body']['type']); } } @@ -3514,7 +3514,7 @@ class DatabasesCustomServerTest extends Scope { $key = 'string'; $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; // Create document to test against $document = $this->client->call( @@ -3526,7 +3526,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'string' => 'string' ], @@ -3578,7 +3578,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'new_string' => 'string' ], @@ -3600,7 +3600,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'string' => 'string' ], @@ -3630,7 +3630,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => 'collection1', + 'collectionId' => 'collection1', 'name' => 'level1', 'documentSecurity' => false, 'permissions' => [ @@ -3646,7 +3646,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => 'collection2', + 'collectionId' => 'collection2', 'name' => 'level2', 'documentSecurity' => false, 'permissions' => [ @@ -3684,7 +3684,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2Id, + 'relatedCollectionId' => $collection2Id, 'type' => 'oneToMany', 'twoWay' => true, 'onDelete' => 'cascade', @@ -3700,11 +3700,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -3712,7 +3712,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'level2' => [[ '$id' => 'unique()', @@ -3764,8 +3764,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -3775,8 +3775,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } @@ -3794,7 +3794,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2Id, + 'relatedCollectionId' => $collection2Id, 'type' => 'oneToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -3810,11 +3810,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -3822,7 +3822,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'level2' => [ '$id' => 'unique()', @@ -3874,8 +3874,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -3885,8 +3885,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } @@ -3904,7 +3904,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2Id, + 'relatedCollectionId' => $collection2Id, 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -3920,11 +3920,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -3932,7 +3932,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'level2' => [ '$id' => 'unique()', @@ -3984,8 +3984,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -3995,8 +3995,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } @@ -4014,7 +4014,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedTableId' => $collection2Id, + 'relatedCollectionId' => $collection2Id, 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -4030,11 +4030,11 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $collection1RelationAttribute = $collection1Attributes['body']['columns'][0]; + $collection1RelationAttribute = $collection1Attributes['body']['attributes'][0]; $this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']); $this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $collection1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']); // Create a document for checking later $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([ @@ -4042,7 +4042,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => [ 'level2' => [ '$id' => 'unique()', @@ -4094,8 +4094,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection1Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection1Attributes['body']['columns'][0]['key']); + $this->assertEquals(1, count($collection1Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']); // Check if attribute was renamed on the child's side $collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [ @@ -4105,8 +4105,8 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $collection2Attributes['headers']['status-code']); - $this->assertEquals(1, count($collection2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $collection2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(1, count($collection2Attributes['body']['attributes'])); + $this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']); $this->cleanupRelationshipCollection(); } diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/Collections/DatabasesPermissionsGuestTest.php similarity index 95% rename from tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php rename to tests/e2e/Services/Databases/Collections/DatabasesPermissionsGuestTest.php index 64413c2fab..d05c944391 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesPermissionsGuestTest.php @@ -1,6 +1,6 @@ client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -42,7 +42,7 @@ class DatabasesPermissionsGuestTest extends Scope ], ]); $privateMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'permissions' => [], 'documentSecurity' => true, @@ -94,14 +94,14 @@ class DatabasesPermissionsGuestTest extends Scope $databaseId = $data['databaseId']; $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], 'permissions' => $permissions, ]); $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -124,11 +124,11 @@ class DatabasesPermissionsGuestTest extends Scope ]); $this->assertEquals(1, $publicDocuments['body']['total']); - $this->assertEquals($permissions, $publicDocuments['body']['rows'][0]['$permissions']); + $this->assertEquals($permissions, $publicDocuments['body']['documents'][0]['$permissions']); if (\in_array(Permission::read(Role::any()), $permissions)) { $this->assertEquals(1, $privateDocuments['body']['total']); - $this->assertEquals($permissions, $privateDocuments['body']['rows'][0]['$permissions']); + $this->assertEquals($permissions, $privateDocuments['body']['documents'][0]['$permissions']); } else { $this->assertEquals(0, $privateDocuments['body']['total']); } @@ -152,7 +152,7 @@ class DatabasesPermissionsGuestTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ] @@ -165,7 +165,7 @@ class DatabasesPermissionsGuestTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -175,7 +175,7 @@ class DatabasesPermissionsGuestTest extends Scope // Create a document in private collection with API key so we can test that update and delete are also not allowed $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -241,7 +241,7 @@ class DatabasesPermissionsGuestTest extends Scope $databaseId = $database['body']['$id']; $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::create(Role::any()), @@ -263,7 +263,7 @@ class DatabasesPermissionsGuestTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Thor: Ragnarok', ], diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/Collections/DatabasesPermissionsMemberTest.php similarity index 96% rename from tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php rename to tests/e2e/Services/Databases/Collections/DatabasesPermissionsMemberTest.php index 2b959344fe..e8065501fe 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesPermissionsMemberTest.php @@ -1,6 +1,6 @@ client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -146,7 +146,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(202, $response['headers']['status-code']); $private = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Private Movies', 'permissions' => [ Permission::read(Role::users()), @@ -167,7 +167,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(202, $response['headers']['status-code']); $doconly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Document Only Movies', 'permissions' => [], 'documentSecurity' => true, @@ -186,7 +186,7 @@ class DatabasesPermissionsMemberTest extends Scope return [ 'users' => $this->users, - 'tables' => $this->collections, + 'collections' => $this->collections, 'databaseId' => $databaseId ]; } @@ -199,11 +199,11 @@ class DatabasesPermissionsMemberTest extends Scope public function testReadDocuments($permissions, $anyCount, $usersCount, $docOnlyCount, $data) { $users = $data['users']; - $collections = $data['tables']; + $collections = $data['collections']; $databaseId = $data['databaseId']; $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['public'] . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -212,7 +212,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['private'] . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -221,7 +221,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collections['doconly'] . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsScope.php b/tests/e2e/Services/Databases/Collections/DatabasesPermissionsScope.php similarity index 98% rename from tests/e2e/Services/Databases/DatabasesPermissionsScope.php rename to tests/e2e/Services/Databases/Collections/DatabasesPermissionsScope.php index 0042d253ac..9d46dda5c6 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsScope.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesPermissionsScope.php @@ -1,6 +1,6 @@ assertEquals(201, $db['headers']['status-code']); $collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::custom('collection1'), + 'collectionId' => ID::custom('collection1'), 'name' => 'Collection 1', 'permissions' => [ Permission::read(Role::team($teams['team1']['$id'])), @@ -64,7 +64,7 @@ class DatabasesPermissionsTeamTest extends Scope ]); $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections', $this->getServerHeader(), [ - 'tableId' => ID::custom('collection2'), + 'collectionId' => ID::custom('collection2'), 'name' => 'Collection 2', 'permissions' => [ Permission::read(Role::team($teams['team2']['$id'])), @@ -141,7 +141,7 @@ class DatabasesPermissionsTeamTest extends Scope $this->createCollections($this->teams); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections/' . $this->collections['collection1'] . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], @@ -149,7 +149,7 @@ class DatabasesPermissionsTeamTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/collections/' . $this->collections['collection2'] . '/documents', $this->getServerHeader(), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Ipsum', ], @@ -174,7 +174,7 @@ class DatabasesPermissionsTeamTest extends Scope ]); if ($success) { - $this->assertCount(1, $documents['body']['rows']); + $this->assertCount(1, $documents['body']['documents']); } else { $this->assertEquals(401, $documents['headers']['status-code']); } @@ -192,7 +192,7 @@ class DatabasesPermissionsTeamTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'], ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'title' => 'Ipsum', ], From 2d74765ba0fd68026c73e38ac1505bd728480a15 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 8 May 2025 20:00:01 +0530 Subject: [PATCH 078/173] update: fix exception. --- .github/workflows/tests.yml | 4 ++-- .../Http/Databases/Collections/Attributes/Action.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a7fc1cf0c6..4775272185 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,7 +145,7 @@ jobs: Account, Avatars, Console, - Databases, + Databases/Collections, Functions, FunctionsSchedule, GraphQL, @@ -213,7 +213,7 @@ jobs: Account, Avatars, Console, - Databases, + Databases/Collections, Functions, FunctionsSchedule, GraphQL, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index fcb9bcc2df..be6e3d1ff7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -494,7 +494,7 @@ abstract class Action extends UtopiaAction } if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($this->getTypeInvalidException(), $validator->getDescription()); + throw new Exception($this->getInvalidValueException(), $validator->getDescription()); } $options = [ From 0ecd97d027cb03bb8cb0bd059abecc54df182cf4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 09:33:52 +0530 Subject: [PATCH 079/173] fix: wrong param. --- .../Http/Databases/Tables/Columns/Relationship/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index 2e338ae037..be5da989af 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -80,7 +80,7 @@ class Create extends RelationshipCreate ->inject('queueForDatabase') ->inject('queueForEvents') ->callback(function (string $databaseId, string $tableId, string $relatedTableId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $relatedTableId, $type, $twoWayKey, $key, $twoWayKey, $onDelete, $response, $dbForProject, $queueForDatabase, $queueForEvents); + parent::action($databaseId, $tableId, $relatedTableId, $type, $twoWay, $key, $twoWayKey, $onDelete, $response, $dbForProject, $queueForDatabase, $queueForEvents); }); } } From ef30e827c75a4c1885a4dbc08af04005e5294ca1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 09:52:04 +0530 Subject: [PATCH 080/173] misc: changes to the databases worker. --- .../Modules/Databases/Workers/Databases.php | 293 +++++++++--------- 1 file changed, 146 insertions(+), 147 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 2a6d1f1f28..8711d28d2c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -60,8 +60,8 @@ class Databases extends Action } $type = $payload['type']; - $row = new Document($payload['row'] ?? $payload['document'] ?? []); - $table = new Document($payload['table'] ?? $payload['collection'] ?? []); + $document = new Document($payload['row'] ?? $payload['document'] ?? []); + $collection = new Document($payload['table'] ?? $payload['collection'] ?? []); $database = new Document($payload['database'] ?? []); $log->addTag('projectId', $project->getId()); @@ -74,20 +74,20 @@ class Databases extends Action $log->addTag('databaseId', $database->getId()); match (\strval($type)) { - DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $project, $dbForProject), - DATABASE_TYPE_DELETE_COLLECTION => $this->deleteTable($database, $table, $project, $dbForProject), - DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createColumn($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteColumn($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $table, $row, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $dbForProject), + DATABASE_TYPE_DELETE_COLLECTION => $this->deleteCollection($database, $collection, $dbForProject), + DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), default => throw new Exception('No database operation for type: ' . \strval($type)), }; } /** * @param Document $database - * @param Document $table - * @param Document $column + * @param Document $collection + * @param Document $attribute * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject @@ -98,20 +98,20 @@ class Databases extends Action * @throws \Exception * @throws \Throwable */ - private function createColumn( + private function createAttribute( Document $database, - Document $table, - Document $column, + Document $collection, + Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime ): void { - if ($table->isEmpty()) { - throw new Exception('Missing table'); + if ($collection->isEmpty()) { + throw new Exception('Missing collection/table'); } - if ($column->isEmpty()) { - throw new Exception('Missing column'); + if ($attribute->isEmpty()) { + throw new Exception('Missing attribute/column'); } $projectId = $project->getId(); @@ -123,42 +123,42 @@ class Databases extends Action * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing information. */ - $column = $dbForProject->getDocument('attributes', $column->getId()); + $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); - if ($column->isEmpty()) { + if ($attribute->isEmpty()) { // Attribute was deleted before job was processed return; } - $tableId = $table->getId(); - $key = $column->getAttribute('key', ''); - $type = $column->getAttribute('type', ''); - $size = $column->getAttribute('size', 0); - $required = $column->getAttribute('required', false); - $default = $column->getAttribute('default', null); - $signed = $column->getAttribute('signed', true); - $array = $column->getAttribute('array', false); - $format = $column->getAttribute('format', ''); - $formatOptions = $column->getAttribute('formatOptions', []); - $filters = $column->getAttribute('filters', []); - $options = $column->getAttribute('options', []); + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $type = $attribute->getAttribute('type', ''); + $size = $attribute->getAttribute('size', 0); + $required = $attribute->getAttribute('required', false); + $default = $attribute->getAttribute('default', null); + $signed = $attribute->getAttribute('signed', true); + $array = $attribute->getAttribute('array', false); + $format = $attribute->getAttribute('format', ''); + $formatOptions = $attribute->getAttribute('formatOptions', []); + $filters = $attribute->getAttribute('filters', []); + $options = $attribute->getAttribute('options', []); $project = $dbForPlatform->getDocument('projects', $projectId); - $relatedColumn = new Document(); - $relatedTable = new Document(); + $relatedAttribute = new Document(); + $relatedCollection = new Document(); try { switch ($type) { case Database::VAR_RELATIONSHIP: - $relatedTable = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedTable->isEmpty()) { - throw new DatabaseException('Table not found'); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedCollection->isEmpty()) { + throw new DatabaseException('Collection/Table not found'); } if ( !$dbForProject->createRelationship( - collection: 'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), - relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), + collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), type: $options['relationType'], twoWay: $options['twoWay'], id: $key, @@ -166,61 +166,61 @@ class Databases extends Action onDelete: $options['onDelete'], ) ) { - throw new DatabaseException('Failed to create Column'); + throw new DatabaseException('Failed to create attribute/column'); } if ($options['twoWay']) { - $relatedColumn = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); - $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'available')); + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available')); } break; default: - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Column'); + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create attribute/column'); } } - $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'available')); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); } catch (\Throwable $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { - $column->setAttribute('error', $e->getMessage()); - if (! $relatedColumn->isEmpty()) { - $relatedColumn->setAttribute('error', $e->getMessage()); + $attribute->setAttribute('error', $e->getMessage()); + if (! $relatedAttribute->isEmpty()) { + $relatedAttribute->setAttribute('error', $e->getMessage()); } } $dbForProject->updateDocument( 'attributes', - $column->getId(), - $column->setAttribute('status', 'failed') + $attribute->getId(), + $attribute->setAttribute('status', 'failed') ); - if (! $relatedColumn->isEmpty()) { + if (! $relatedAttribute->isEmpty()) { $dbForProject->updateDocument( 'attributes', - $relatedColumn->getId(), - $relatedColumn->setAttribute('status', 'failed') + $relatedAttribute->getId(), + $relatedAttribute->setAttribute('status', 'failed') ); } throw $e; } finally { - $this->trigger($database, $table, $project, $events, $queueForRealtime, $column); + $this->trigger($database, $collection, $project, $events, $queueForRealtime, $attribute); - if (! $relatedTable->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); + if (! $relatedCollection->isEmpty()) { + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $tableId); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } /** * @param Document $database - * @param Document $table - * @param Document $column + * @param Document $collection + * @param Document $attribute * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject @@ -231,13 +231,13 @@ class Databases extends Action * @throws \Exception * @throws \Throwable **/ - private function deleteColumn(Document $database, Document $table, Document $column, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($table->isEmpty()) { - throw new Exception('Missing collection'); + if ($collection->isEmpty()) { + throw new Exception('Missing collection/table'); } - if ($column->isEmpty()) { - throw new Exception('Missing attribute'); + if ($attribute->isEmpty()) { + throw new Exception('Missing attribute/column'); } $projectId = $project->getId(); @@ -245,13 +245,13 @@ class Databases extends Action 'databases.[databaseId].tables.[tableId].columns.[columnId].delete', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', ]; - $tableId = $table->getId(); - $key = $column->getAttribute('key', ''); - $type = $column->getAttribute('type', ''); + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $type = $attribute->getAttribute('type', ''); $project = $dbForPlatform->getDocument('projects', $projectId); - $options = $column->getAttribute('options', []); - $relatedColumn = new Document(); - $relatedTable = new Document(); + $options = $attribute->getAttribute('options', []); + $relatedAttribute = new Document(); + $relatedCollection = new Document(); // possible states at this point: // - available: should not land in queue; controller flips these to 'deleting' // - processing: hasn't finished creating @@ -263,89 +263,89 @@ class Databases extends Action try { if ($type === Database::VAR_RELATIONSHIP) { if ($options['twoWay']) { - $relatedTable = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedTable->isEmpty()) { - throw new DatabaseException('Table not found'); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedCollection->isEmpty()) { + throw new DatabaseException('Collection/Table not found'); } - $relatedColumn = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']); + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); } - if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { - $dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'stuck')); + if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); throw new DatabaseException('Failed to delete Relationship'); } - } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { - throw new DatabaseException('Failed to delete Column'); + } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new DatabaseException('Failed to delete attribute/column'); } - $dbForProject->deleteDocument('attributes', $column->getId()); + $dbForProject->deleteDocument('attributes', $attribute->getId()); - if (!$relatedColumn->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedColumn->getId()); + if (!$relatedAttribute->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); } } catch (NotFound $e) { Console::error($e->getMessage()); - $dbForProject->deleteDocument('attributes', $column->getId()); + $dbForProject->deleteDocument('attributes', $attribute->getId()); - if (!$relatedColumn->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedColumn->getId()); + if (!$relatedAttribute->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); } } catch (\Throwable $e) { Console::error($e->getMessage()); if ($e instanceof DatabaseException) { - $column->setAttribute('error', $e->getMessage()); - if (!$relatedColumn->isEmpty()) { - $relatedColumn->setAttribute('error', $e->getMessage()); + $attribute->setAttribute('error', $e->getMessage()); + if (!$relatedAttribute->isEmpty()) { + $relatedAttribute->setAttribute('error', $e->getMessage()); } } $dbForProject->updateDocument( 'attributes', - $column->getId(), - $column->setAttribute('status', 'stuck') + $attribute->getId(), + $attribute->setAttribute('status', 'stuck') ); - if (!$relatedColumn->isEmpty()) { + if (!$relatedAttribute->isEmpty()) { $dbForProject->updateDocument( 'attributes', - $relatedColumn->getId(), - $relatedColumn->setAttribute('status', 'stuck') + $relatedAttribute->getId(), + $relatedAttribute->setAttribute('status', 'stuck') ); } throw $e; } finally { - $this->trigger($database, $table, $project, $events, $queueForRealtime, $column); + $this->trigger($database, $collection, $project, $events, $queueForRealtime, $attribute); } // The underlying database removes/rebuilds indexes when attribute is removed - // Update indexes table with changes + // Update indexes collection with changes /** @var Document[] $indexes */ - $indexes = $table->getAttribute('indexes', []); + $indexes = $collection->getAttribute('indexes', []); foreach ($indexes as $index) { - /** @var string[] $columns */ - $columns = $index->getAttribute('attributes'); + /** @var string[] $attributes */ + $attributes = $index->getAttribute('attributes'); $lengths = $index->getAttribute('lengths'); $orders = $index->getAttribute('orders'); - $found = \array_search($key, $columns); + $found = \array_search($key, $attributes); if ($found !== false) { // If found, remove entry from attributes, lengths, and orders // array_values wraps array_diff to reindex array keys // when found attribute is removed from array - $columns = \array_values(\array_diff($columns, [$columns[$found]])); + $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); $lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : [])); $orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : [])); - if (empty($columns)) { + if (empty($attributes)) { $dbForProject->deleteDocument('indexes', $index->getId()); } else { $index - ->setAttribute('attributes', $columns, Document::SET_TYPE_ASSIGN) + ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); @@ -363,7 +363,7 @@ class Databases extends Action } if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $table, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); + $this->deleteIndex($database, $collection, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); } else { $dbForProject->updateDocument('indexes', $index->getId(), $index); } @@ -371,17 +371,17 @@ class Databases extends Action } } } finally { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $tableId); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); - if (! $relatedTable->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedTable->getId()); + if (! $relatedCollection->isEmpty()) { + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); } } } /** * @param Document $database - * @param Document $table + * @param Document $collection * @param Document $index * @param Document $project * @param Database $dbForPlatform @@ -394,10 +394,10 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function createIndex(Document $database, Document $table, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($table->isEmpty()) { - throw new Exception('Missing collection'); + if ($collection->isEmpty()) { + throw new Exception('Missing collection/table'); } if ($index->isEmpty()) { throw new Exception('Missing index'); @@ -408,7 +408,7 @@ class Databases extends Action 'databases.[databaseId].tables.[tableId].indexes.[indexId].update', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update', ]; - $collectionId = $table->getId(); + $collectionId = $collection->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); $attributes = $index->getAttribute('attributes', []); @@ -417,7 +417,7 @@ class Databases extends Action $project = $dbForPlatform->getDocument('projects', $projectId); try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { throw new DatabaseException('Failed to create Index'); } $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); @@ -434,14 +434,14 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $table, $project, $events, $queueForRealtime, null, $index); + $this->trigger($database, $collection, $project, $events, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } /** * @param Document $database - * @param Document $table + * @param Document $collection * @param Document $index * @param Document $project * @param Database $dbForPlatform @@ -454,10 +454,10 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function deleteIndex(Document $database, Document $table, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void + private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { - if ($table->isEmpty()) { - throw new Exception('Missing collection'); + if ($collection->isEmpty()) { + throw new Exception('Missing collection/table'); } if ($index->isEmpty()) { throw new Exception('Missing index'); @@ -473,7 +473,7 @@ class Databases extends Action $project = $dbForPlatform->getDocument('projects', $projectId); try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $key)) { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); @@ -493,22 +493,21 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $table, $project, $events, $queueForRealtime, null, $index); - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $table->getId()); + $this->trigger($database, $collection, $project, $events, $queueForRealtime, null, $index); + $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } } /** * @param Document $database - * @param Document $project * @param $dbForProject * @return void * @throws Exception */ - protected function deleteDatabase(Document $database, Document $project, $dbForProject): void + protected function deleteDatabase(Document $database, $dbForProject): void { - $this->deleteByGroup('database_' . $database->getInternalId(), [], $dbForProject, function ($collection) use ($database, $project, $dbForProject) { - $this->deleteTable($database, $collection, $project, $dbForProject); + $this->deleteByGroup('database_' . $database->getInternalId(), [], $dbForProject, function ($collection) use ($database, $dbForProject) { + $this->deleteCollection($database, $collection, $dbForProject); }); $dbForProject->deleteCollection('database_' . $database->getInternalId()); @@ -516,7 +515,7 @@ class Databases extends Action /** * @param Document $database - * @param Document $table + * @param Document $collection * @param Document $project * @param Database $dbForProject * @return void @@ -527,17 +526,17 @@ class Databases extends Action * @throws Structure * @throws Exception */ - protected function deleteTable(Document $database, Document $table, Document $project, Database $dbForProject): void + protected function deleteCollection(Document $database, Document $collection, Database $dbForProject): void { - if ($table->isEmpty()) { - throw new Exception('Missing table'); + if ($collection->isEmpty()) { + throw new Exception('Missing collection/table'); } - $collectionId = $table->getId(); - $collectionInternalId = $table->getInternalId(); + $collectionId = $collection->getId(); + $collectionInternalId = $collection->getInternalId(); $databaseInternalId = $database->getInternalId(); - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $table->getInternalId()); + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); /** * Related collections relating to current collection @@ -551,9 +550,9 @@ class Databases extends Action Query::contains('options', ['"relatedCollection":"'. $collectionId .'"']), ], $dbForProject, - function ($column) use ($dbForProject, $databaseInternalId) { - $dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $column->getAttribute('collectionId')); - $dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $column->getAttribute('collectionInternalId')); + function ($attribute) use ($dbForProject, $databaseInternalId) { + $dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $attribute->getAttribute('collectionId')); + $dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $attribute->getAttribute('collectionInternalId')); } ); @@ -570,52 +569,52 @@ class Databases extends Action /** - * @param string $tableId + * @param string $collectionId * @param array $queries * @param Database $database * @param callable|null $callback * @return void * @throws Exception */ - protected function deleteByGroup(string $tableId, array $queries, Database $database, callable $callback = null): void + protected function deleteByGroup(string $collectionId, array $queries, Database $database, callable $callback = null): void { $start = \microtime(true); try { $count = $database->deleteDocuments( - $tableId, + $collectionId, $queries, Database::DELETE_BATCH_SIZE, $callback ); } catch (\Throwable $th) { $tenant = $database->getSharedTables() ? 'Tenant:'.$database->getTenant() : ''; - Console::error("Failed to delete rows for table:{$database->getNamespace()}_{$tableId} {$tenant} :{$th->getMessage()}"); + Console::error("Failed to delete documents/rows for collection/table: {$database->getNamespace()}_{$collectionId} {$tenant} :{$th->getMessage()}"); return; } $end = \microtime(true); - Console::info("Deleted {$count} rows by group in " . ($end - $start) . " seconds"); + Console::info("Deleted {$count} documents/rows by group in " . ($end - $start) . " seconds"); } /** * @param Document $database - * @param Document $table + * @param Document $collection * @param Document $project * @param string[] $events * @param Realtime $queueForRealtime - * @param Document|null $column + * @param Document|null $attribute * @param Document|null $index * @return void * @throws DatabaseException */ protected function trigger( Document $database, - Document $table, + Document $collection, Document $project, array $events, Realtime $queueForRealtime, - Document|null $column = null, + Document|null $attribute = null, Document|null $index = null, ): void { // table and collection @@ -624,15 +623,15 @@ class Databases extends Action ->setProject($project) ->setSubscribers(['console']) ->setEvent($event) - ->setParam('tableId', $table->getId()) - ->setParam('collectionId', $table->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setParam('databaseId', $database->getId()); - if (! empty($column)) { + if (! empty($attribute)) { $queueForRealtime - ->setParam('columnId', $column->getId()) - ->setParam('attributeId', $column->getId()) - ->setPayload($column->getArrayCopy()); + ->setParam('columnId', $attribute->getId()) + ->setParam('attributeId', $attribute->getId()) + ->setPayload($attribute->getArrayCopy()); } if (! empty($index)) { From 938b376f3c811591b5f405123a0838e19908d3fd Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 10:02:14 +0530 Subject: [PATCH 081/173] fix: var ref. --- .../Databases/Http/Databases/Collections/Documents/Update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 9b373c7e17..eaaa2bb63f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -287,7 +287,7 @@ class Update extends Action $response->dynamic($document, $this->getResponseModel()); $relationships = \array_map( - fn ($row) => $document->getAttribute('key'), + fn ($document) => $document->getAttribute('key'), \array_filter( $collection->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP From 65dbebd103467027f7118dc84cb5896d639f43fd Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 11:09:58 +0530 Subject: [PATCH 082/173] misc: fix error types. --- .../Collections/Attributes/Action.php | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index be6e3d1ff7..d1047e671d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -150,6 +150,16 @@ abstract class Action extends UtopiaAction : Exception::COLUMN_LIMIT_EXCEEDED; } + /** + * Get the appropriate index invalid exception. + */ + final protected function getInvalidIndexException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_INVALID + : Exception::COLUMN_INDEX_INVALID; + } + /** * Get the correct default unsupported message. */ @@ -480,7 +490,7 @@ abstract class Action extends UtopiaAction $max ??= $attribute->getAttribute('formatOptions')['max']; if ($min > $max) { - throw new Exception($this->getTypeInvalidException(), 'Minimum value must be lesser than maximum value'); + throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value'); } if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) { @@ -506,17 +516,17 @@ abstract class Action extends UtopiaAction break; case APP_DATABASE_ATTRIBUTE_ENUM: if (empty($elements)) { - throw new Exception($this->getTypeInvalidException(), 'Enum elements must not be empty'); + throw new Exception($this->getInvalidValueException(), 'Enum elements must not be empty'); } foreach ($elements as $element) { if (\strlen($element) === 0) { - throw new Exception($this->getTypeInvalidException(), 'Each enum element must not be empty'); + throw new Exception($this->getInvalidValueException(), 'Each enum element must not be empty'); } } if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception($this->getTypeInvalidException(), 'Default value not found in elements'); + throw new Exception($this->getInvalidValueException(), 'Default value not found in elements'); } $options = [ @@ -575,7 +585,7 @@ abstract class Action extends UtopiaAction } catch (LimitException) { throw new Exception($this->getLimitException()); } catch (IndexException $e) { - throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); + throw new Exception($this->getInvalidIndexException(), $e->getMessage()); } } From a8dc5c3797532e8a50412574a3e0cd0f103a2eec Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 11:10:53 +0530 Subject: [PATCH 083/173] add: table tests to usage. --- tests/e2e/General/UsageTest.php | 296 +++++++++++++++++++++++++++++--- 1 file changed, 273 insertions(+), 23 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 54b8597204..1cdd2070d0 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -411,13 +411,13 @@ class UsageTest extends Scope } /** @depends testStorageStats */ - public function testPrepareDatabaseStats(array $data): array + public function testPrepareDatabaseStatsCollectionsAPI(array $data): array { $requestsTotal = $data['requestsTotal']; $databasesTotal = 0; - $tablesTotal = 0; - $rowsTotal = 0; + $collectionsTotal = 0; + $documentsTotal = 0; for ($i = 0; $i < self::CREATE; $i++) { $name = uniqid() . ' database'; @@ -470,7 +470,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'tableId' => 'unique()', + 'collectionId' => 'unique()', 'name' => $name, 'documentSecurity' => false, 'permissions' => [ @@ -486,7 +486,7 @@ class UsageTest extends Scope $this->assertNotEmpty($response['body']['$id']); $requestsTotal += 1; - $tablesTotal += 1; + $collectionsTotal += 1; $collectionId = $response['body']['$id']; @@ -501,7 +501,7 @@ class UsageTest extends Scope $this->assertEmpty($response['body']); - $tablesTotal -= 1; + $collectionsTotal -= 1; $requestsTotal += 1; } } @@ -537,7 +537,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'rowId' => 'unique()', + 'documentId' => 'unique()', 'data' => ['name' => $name] ] ); @@ -546,7 +546,7 @@ class UsageTest extends Scope $this->assertNotEmpty($response['body']['$id']); $requestsTotal += 1; - $rowsTotal += 1; + $documentsTotal += 1; $documentId = $response['body']['$id']; @@ -561,31 +561,31 @@ class UsageTest extends Scope $this->assertEmpty($response['body']); - $rowsTotal -= 1; + $documentsTotal -= 1; $requestsTotal += 1; } } return array_merge($data, [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, + 'collectionId' => $collectionId, 'requestsTotal' => $requestsTotal, 'databasesTotal' => $databasesTotal, - 'tablesTotal' => $tablesTotal, - 'rowsTotal' => $rowsTotal, + 'collectionsTotal' => $collectionsTotal, + 'documentsTotal' => $documentsTotal, ]); } - /** @depends testPrepareDatabaseStats */ + /** @depends testPrepareDatabaseStatsCollectionsAPI */ #[Retry(count: 1)] - public function testDatabaseStats(array $data): array + public function testDatabaseStatsCollectionsAPI(array $data): array { $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $requestsTotal = $data['requestsTotal']; $databasesTotal = $data['databasesTotal']; - $tablesTotal = $data['tablesTotal']; - $rowsTotal = $data['rowsTotal']; + $collectionsTotal = $data['collectionsTotal']; + $documentsTotal = $data['documentsTotal']; sleep(self::WAIT); @@ -606,7 +606,7 @@ class UsageTest extends Scope $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); $this->validateDates($response['body']['requests']); $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); - $this->assertEquals($rowsTotal, $response['body']['rowsTotal']); + $this->assertEquals($documentsTotal, $response['body']['documentsTotal']); $response = $this->client->call( Client::METHOD_GET, @@ -616,9 +616,260 @@ class UsageTest extends Scope $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); $this->validateDates($response['body']['databases']); - $this->assertEquals($tablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); + $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); + $this->validateDates($response['body']['collections']); + $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); + $this->validateDates($response['body']['documents']); + + $response = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/usage?range=30d', + $this->getConsoleHeaders() + ); + + $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); + $this->validateDates($response['body']['collections']); + + $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); + $this->validateDates($response['body']['documents']); + + $response = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); + + $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); + $this->validateDates($response['body']['documents']); + + return $data; + } + + /** @depends testDatabaseStatsCollectionsAPI */ + public function testPrepareDatabaseStatsTablesAPI(array $data): array + { + $rowsTotal = 0; + $tablesTotal = 0; + $databasesTotal = $data['databasesTotal']; + $documentsTotal = $data['documentsTotal']; + $collectionsTotal = $data['collectionsTotal']; + + $requestsTotal = $data['requestsTotal']; + + for ($i = 0; $i < self::CREATE; $i++) { + $name = uniqid() . ' database'; + + $response = $this->client->call( + Client::METHOD_POST, + '/databases', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'databaseId' => 'unique()', + 'name' => $name, + ] + ); + + $this->assertEquals($name, $response['body']['name']); + $this->assertNotEmpty($response['body']['$id']); + + $requestsTotal += 1; + $databasesTotal += 1; + + $databaseId = $response['body']['$id']; + + if ($i < (self::CREATE / 2)) { + $response = $this->client->call( + Client::METHOD_DELETE, + '/databases/' . $databaseId, + array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + ); + + $this->assertEmpty($response['body']); + + $databasesTotal -= 1; + $requestsTotal += 1; + } + } + + for ($i = 0; $i < self::CREATE; $i++) { + $name = uniqid() . ' table'; + + $response = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'tableId' => 'unique()', + 'name' => $name, + 'documentSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ] + ); + + $this->assertEquals($name, $response['body']['name']); + $this->assertNotEmpty($response['body']['$id']); + + $requestsTotal += 1; + $tablesTotal += 1; + + $tableId = $response['body']['$id']; + + if ($i < (self::CREATE / 2)) { + $response = $this->client->call( + Client::METHOD_DELETE, + '/databases/' . $databaseId . '/tables/' . $tableId, + array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + ); + + $this->assertEmpty($response['body']); + + $tablesTotal -= 1; + $requestsTotal += 1; + } + } + + $response = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/columns' . '/string', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ] + ); + + $this->assertEquals('name', $response['body']['key']); + + sleep(self::WAIT); + + $requestsTotal += 1; + + for ($i = 0; $i < self::CREATE; $i++) { + $name = uniqid() . ' table'; + + $response = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'rowId' => 'unique()', + 'data' => ['name' => $name] + ] + ); + + $this->assertEquals($name, $response['body']['name']); + $this->assertNotEmpty($response['body']['$id']); + + $requestsTotal += 1; + $rowsTotal += 1; + + $rowId = $response['body']['$id']; + + if ($i < (self::CREATE / 2)) { + $response = $this->client->call( + Client::METHOD_DELETE, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, + array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + ); + + $this->assertEmpty($response['body']); + + $rowsTotal -= 1; + $requestsTotal += 1; + } + } + + return array_merge($data, [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'requestsTotal' => $requestsTotal, + 'databasesTotal' => $databasesTotal, + 'tablesTotal' => $tablesTotal, + 'rowsTotal' => $rowsTotal, + + // For clarity + 'absoluteRowsTotal' => $rowsTotal + $data['documentsTotal'], + 'absoluteTablesTotal' => $tablesTotal + $data['collectionsTotal'], + ]); + } + + /** @depends testPrepareDatabaseStatsTablesAPI */ + #[Retry(count: 1)] + public function testDatabaseStatsTablesAPI(array $data): array + { + $tableId = $data['tableId']; + $databaseId = $data['databaseId']; + $requestsTotal = $data['requestsTotal']; + + $absoluteRowsTotal = $data['absoluteRowsTotal']; + $absoluteTablesTotal = $data['absoluteTablesTotal']; + + $rowsTotal = $data['rowsTotal']; + $tablesTotal = $data['tablesTotal']; + $databasesTotal = $data['databasesTotal']; + + sleep(self::WAIT); + + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1d', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); + + $this->assertGreaterThanOrEqual(31, count($response['body'])); + $this->assertCount(1, $response['body']['requests']); + $this->assertCount(1, $response['body']['network']); + $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); + $this->validateDates($response['body']['requests']); + $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); + + // project level includes all i.e. documents + rows total. + $this->assertEquals($absoluteRowsTotal, $response['body']['rowsTotal']); + + $response = $this->client->call( + Client::METHOD_GET, + '/databases/usage?range=30d', + $this->getConsoleHeaders() + ); + + $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); + $this->validateDates($response['body']['databases']); + + // database level includes all i.e. collections + tables total. + $this->assertEquals($absoluteTablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); // database level $this->validateDates($response['body']['tables']); - $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + + // database level includes all i.e. documents + rows total. + $this->assertEquals($absoluteRowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); $this->validateDates($response['body']['rows']); $response = $this->client->call( @@ -635,7 +886,7 @@ class UsageTest extends Scope $response = $this->client->call( Client::METHOD_GET, - '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', + '/databases/' . $databaseId . '/tables/' . $tableId . '/usage?range=30d', $this->getConsoleHeaders() ); @@ -645,7 +896,7 @@ class UsageTest extends Scope return $data; } - /** @depends testDatabaseStats */ + /** @depends testDatabaseStatsTablesAPI */ public function testPrepareFunctionsStats(array $data): array { $executionTime = 0; @@ -859,7 +1110,6 @@ class UsageTest extends Scope return $data; } - public function testPrepareSitesStats(): array { $siteId = $this->setupSite([ From a807dd97d9e5e7c47618dfb70a89b08911a25b0d Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 14:47:01 +0530 Subject: [PATCH 084/173] fix: realtime events. --- src/Appwrite/Event/Realtime.php | 33 +++++++- src/Appwrite/Messaging/Adapter/Realtime.php | 12 +-- .../Collections/Attributes/Action.php | 30 ++------ .../Collections/Attributes/Delete.php | 7 +- .../Collections/Documents/Action.php | 16 ---- .../Collections/Documents/Create.php | 6 +- .../Collections/Documents/Delete.php | 6 +- .../Collections/Documents/Update.php | 6 +- .../Databases/Collections/Indexes/Action.php | 16 ---- .../Databases/Collections/Indexes/Create.php | 5 +- .../Databases/Collections/Indexes/Delete.php | 5 +- .../Modules/Databases/Workers/Databases.php | 76 ++++++++----------- 12 files changed, 95 insertions(+), 123 deletions(-) diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index 67d51e5c52..f45b6548d8 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -73,21 +73,46 @@ class Realtime extends Event } $allEvents = Event::generateEvents($this->getEvent(), $this->getParams()); + $firstEvent = $allEvents[0]; // most verbose event pattern + + // generate and merge all collection and tables api events. + if (str_contains($this->getEvent(), 'databases.') && str_contains($firstEvent, 'collections')) { + $tableEventMap = [ + 'collections' => 'tables', 'attributes' => 'columns', + 'attributeId' => 'columnId', 'documents' => 'rows', 'documentId' => 'rowId', + ]; + + // replace params! + $tableEvent = str_replace( + array_keys($tableEventMap), + array_values($tableEventMap), + $this->getEvent() + ); + + // generate new events + $tableEvents = Event::generateEvents($tableEvent, $this->getParams()); + + // merge all of the api events + $allEvents = array_merge($allEvents, $tableEvents); + + // remove duplicates + $allEvents = array_values(array_unique($allEvents)); + } + $payload = new Document($this->getPayload()); $db = $this->getContext('database'); $bucket = $this->getContext('bucket'); - // can be Tables API or Collections API, generated channels include both! + // Can be Tables API or Collections API; generated channels include both! $tableOrCollection = $this->getContext('table') ?? $this->getContext('collection'); $target = RealtimeAdapter::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], + event: $firstEvent, payload: $payload, project: $this->getProject(), database: $db, - table: $tableOrCollection, + collection: $tableOrCollection, bucket: $bucket, ); diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 4980618ab1..bfe87c49db 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -252,12 +252,12 @@ class Realtime extends Adapter * @param Document $payload * @param Document|null $project * @param Document|null $database - * @param Document|null $table + * @param Document|null $collection * @param Document|null $bucket * @return array * @throws \Exception */ - public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $table = null, Document $bucket = null): array + public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $collection = null, Document $bucket = null): array { $channels = []; $roles = []; @@ -308,7 +308,7 @@ class Realtime extends Adapter if ($database->isEmpty()) { throw new \Exception('Database needs to be passed to Realtime for Document/Row events in the Database.'); } - if ($table->isEmpty()) { + if ($collection->isEmpty()) { throw new \Exception('Collection or the Table needs to be passed to Realtime for Document/Row events in the Database.'); } @@ -322,9 +322,9 @@ class Realtime extends Adapter $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents'; $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId(); - $roles = $table->getAttribute('documentSecurity', false) - ? \array_merge($table->getRead(), $payload->getRead()) - : $table->getRead(); + $roles = $collection->getAttribute('documentSecurity', false) + ? \array_merge($collection->getRead(), $payload->getRead()) + : $collection->getRead(); } break; case 'buckets': diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index d1047e671d..954e997872 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -84,22 +84,6 @@ abstract class Action extends UtopiaAction return $this->isCollectionsAPI() ? 'collections' : 'tables'; } - /** - * Get the correct parent param key (e.g. `tableId` or `collectionId`) - */ - final protected function getParentEventsParamKey(): string - { - return $this->isCollectionsAPI() ? 'collectionId' : 'tableId'; - } - - /** - * Get the correct param key (e.g. `attributeId` or `columnId`) - */ - final protected function getEventsParamKey(): string - { - return $this->getContext() . 'Id'; - } - /** * Get the appropriate parent level not found exception. */ @@ -423,9 +407,10 @@ abstract class Action extends UtopiaAction $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam($this->getEventsParamKey(), $attribute->getId()) - // tableId or columnId - ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setParam('attributeId', $attribute->getId()) + ->setParam('columnId', $attribute->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); @@ -623,9 +608,10 @@ abstract class Action extends UtopiaAction $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam($this->getEventsParamKey(), $attribute->getId()) - // tableId or columnId - ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setParam('attributeId', $attribute->getId()) + ->setParam('columnId', $attribute->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); return $attribute; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 2432ac5fd0..81bc77a9ba 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -146,9 +146,10 @@ class Delete extends Action ->setContext('database', $db) ->setParam('databaseId', $databaseId) ->setPayload($response->output($attribute, $model)) - ->setParam($this->getEventsParamKey(), $attribute->getId()) - // tableId or columnId - ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setParam('attributeId', $attribute->getId()) + ->setParam('columnId', $attribute->getId()) + ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) // set proper context ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index b9a3247f96..a5b8fc5805 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -67,22 +67,6 @@ abstract class Action extends UtopiaAction return $this->isCollectionsAPI() ? 'collections' : 'tables'; } - /** - * Get the correct parent param key (e.g. `tableId` or `collectionId`) - */ - final protected function getParentEventsParamKey(): string - { - return $this->isCollectionsAPI() ? 'collectionId' : 'tableId'; - } - - /** - * Get the correct param key (e.g. `documentId` or `rowId`) - */ - final protected function getEventsParamKey(): string - { - return $this->getContext() . 'Id'; - } - /** * Get the appropriate parent level not found exception. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 297d0f3543..4c63837d57 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -310,9 +310,11 @@ class Create extends Action $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) - ->setParam($this->getEventsParamKey(), $document->getId()) + ->setParam('rowId', $document->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setPayload($response->getPayload(), sensitive: $relationships) - ->setParam($this->getParentEventsParamKey(), $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 2f0c4e76db..66e1b37320 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -164,8 +164,10 @@ class Delete extends Action $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) - ->setParam($this->getParentEventsParamKey(), $collection->getId()) - ->setParam($this->getEventsParamKey(), $document->getId()) + ->setParam('rowId', $document->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection) ->setPayload($response->output($document, $this->getResponseModel()), sensitive: $relationships); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index eaaa2bb63f..5bee2ee761 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -297,8 +297,10 @@ class Update extends Action $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) - ->setParam($this->getEventsParamKey(), $document->getId()) - ->setParam($this->getParentEventsParamKey(), $collection->getId()) + ->setParam('rowId', $document->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection) ->setPayload($response->getPayload(), sensitive: $relationships); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index 6186139bfb..93459440f0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -49,22 +49,6 @@ abstract class Action extends UtopiaAction return $this->context; } - /** - * Get the correct grand parent param key (e.g. `tableId` or `collectionId`) - */ - final protected function getGrandParentEventsParamKey(): string - { - return $this->isCollectionsAPI() ? 'collectionId' : 'tableId'; - } - - /** - * Get the key used in event parameters. - */ - final protected function getEventsParamKey(): string - { - return 'indexId'; - } - /** * Determine if the current action is for the Collections API. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 6370a32a4c..014627e0a8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -220,8 +220,9 @@ class Create extends Action $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam($this->getEventsParamKey(), $index->getId()) - ->setParam($this->getGrandParentEventsParamKey(), $collection->getId()) + ->setParam('indexId', $index->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); $response diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index 2d57a8a5f0..f86cf7b11f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -115,9 +115,10 @@ class Delete extends Action $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setParam($this->getEventsParamKey(), $index->getId()) + ->setParam('indexId', $index->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()) ->setPayload($response->output($index, $this->getResponseModel())) - ->setParam($this->getGrandParentEventsParamKey(), $collection->getId()) ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); $response->noContent(); diff --git a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 8711d28d2c..35334de266 100644 --- a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -115,10 +115,8 @@ class Databases extends Action } $projectId = $project->getId(); - $events = [ - "databases.[databaseId].tables.[tableId].columns.[columnId].update", - "databases.[databaseId].collections.[collectionId].attributes.[attributeId].update", - ]; + $event = "databases.[databaseId].collections.[collectionId].attributes.[attributeId].update"; + /** * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing information. @@ -207,7 +205,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $events, $queueForRealtime, $attribute); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); if (! $relatedCollection->isEmpty()) { $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); @@ -241,10 +239,7 @@ class Databases extends Action } $projectId = $project->getId(); - $events = [ - 'databases.[databaseId].tables.[tableId].columns.[columnId].delete', - 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', - ]; + $event = 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete'; $collectionId = $collection->getId(); $key = $attribute->getAttribute('key', ''); $type = $attribute->getAttribute('type', ''); @@ -317,7 +312,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $events, $queueForRealtime, $attribute); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -404,10 +399,7 @@ class Databases extends Action } $projectId = $project->getId(); - $events = [ - 'databases.[databaseId].tables.[tableId].indexes.[indexId].update', - 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update', - ]; + $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update'; $collectionId = $collection->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); @@ -434,7 +426,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $events, $queueForRealtime, null, $index); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } @@ -464,10 +456,7 @@ class Databases extends Action } $projectId = $project->getId(); - $events = [ - 'databases.[databaseId].tables.[tableId].indexes.[indexId].delete', - 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', - ]; + $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete'; $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); $project = $dbForPlatform->getDocument('projects', $projectId); @@ -493,7 +482,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $events, $queueForRealtime, null, $index); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } } @@ -516,7 +505,6 @@ class Databases extends Action /** * @param Document $database * @param Document $collection - * @param Document $project * @param Database $dbForProject * @return void * @throws Authorization @@ -601,7 +589,7 @@ class Databases extends Action * @param Document $database * @param Document $collection * @param Document $project - * @param string[] $events + * @param string $event * @param Realtime $queueForRealtime * @param Document|null $attribute * @param Document|null $index @@ -612,35 +600,31 @@ class Databases extends Action Document $database, Document $collection, Document $project, - array $events, + string $event, Realtime $queueForRealtime, Document|null $attribute = null, Document|null $index = null, ): void { - // table and collection - foreach ($events as $event) { + $queueForRealtime + ->setProject($project) + ->setSubscribers(['console']) + ->setEvent($event) + ->setParam('databaseId', $database->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('collectionId', $collection->getId()); + + if (! empty($attribute)) { $queueForRealtime - ->setProject($project) - ->setSubscribers(['console']) - ->setEvent($event) - ->setParam('tableId', $collection->getId()) - ->setParam('collectionId', $collection->getId()) - ->setParam('databaseId', $database->getId()); - - if (! empty($attribute)) { - $queueForRealtime - ->setParam('columnId', $attribute->getId()) - ->setParam('attributeId', $attribute->getId()) - ->setPayload($attribute->getArrayCopy()); - } - - if (! empty($index)) { - $queueForRealtime - ->setParam('indexId', $index->getId()) - ->setPayload($index->getArrayCopy()); - } - - $queueForRealtime->trigger(); + ->setParam('columnId', $attribute->getId()) + ->setParam('attributeId', $attribute->getId()) + ->setPayload($attribute->getArrayCopy()); } + if (! empty($index)) { + $queueForRealtime + ->setParam('indexId', $index->getId()) + ->setPayload($index->getArrayCopy()); + } + + $queueForRealtime->trigger(); } } From f10be55f92c1615ec8e73a4a0aa1f1cedeffb8de Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 14:49:44 +0530 Subject: [PATCH 085/173] update: short the callbacks. --- .../Http/Databases/Tables/Columns/Boolean/Create.php | 7 +------ .../Http/Databases/Tables/Columns/Boolean/Update.php | 6 +----- .../Http/Databases/Tables/Columns/Datetime/Create.php | 6 +----- .../Http/Databases/Tables/Columns/Datetime/Update.php | 5 +---- .../Databases/Http/Databases/Tables/Columns/Delete.php | 7 +------ .../Http/Databases/Tables/Columns/Email/Create.php | 7 +------ .../Http/Databases/Tables/Columns/Email/Update.php | 6 +----- .../Http/Databases/Tables/Columns/Enum/Create.php | 6 +----- .../Http/Databases/Tables/Columns/Enum/Update.php | 5 +---- .../Http/Databases/Tables/Columns/Float/Create.php | 7 +------ .../Http/Databases/Tables/Columns/Float/Update.php | 6 +----- .../Databases/Http/Databases/Tables/Columns/Get.php | 5 +---- .../Http/Databases/Tables/Columns/IP/Create.php | 7 +------ .../Http/Databases/Tables/Columns/IP/Update.php | 6 +----- .../Http/Databases/Tables/Columns/Integer/Create.php | 7 +------ .../Http/Databases/Tables/Columns/Integer/Update.php | 6 +----- .../Databases/Tables/Columns/Relationship/Create.php | 6 +----- .../Databases/Tables/Columns/Relationship/Update.php | 5 +---- .../Http/Databases/Tables/Columns/String/Create.php | 7 +------ .../Http/Databases/Tables/Columns/String/Update.php | 6 +----- .../Http/Databases/Tables/Columns/URL/Create.php | 7 +------ .../Http/Databases/Tables/Columns/URL/Update.php | 6 +----- .../Databases/Http/Databases/Tables/Columns/XList.php | 5 +---- .../Modules/Databases/Http/Databases/Tables/Create.php | 6 +----- .../Modules/Databases/Http/Databases/Tables/Delete.php | 7 +------ .../Modules/Databases/Http/Databases/Tables/Get.php | 5 +---- .../Databases/Http/Databases/Tables/Indexes/Create.php | 10 +++------- .../Databases/Http/Databases/Tables/Indexes/Delete.php | 7 +------ .../Databases/Http/Databases/Tables/Indexes/Get.php | 5 +---- .../Databases/Http/Databases/Tables/Indexes/XList.php | 5 +---- .../Databases/Http/Databases/Tables/Logs/XList.php | 8 +------- .../Databases/Http/Databases/Tables/Rows/Create.php | 7 +------ .../Databases/Http/Databases/Tables/Rows/Delete.php | 7 +------ .../Databases/Http/Databases/Tables/Rows/Get.php | 6 +----- .../Http/Databases/Tables/Rows/Logs/XList.php | 8 +------- .../Databases/Http/Databases/Tables/Rows/Update.php | 6 +----- .../Databases/Http/Databases/Tables/Rows/XList.php | 6 +----- .../Modules/Databases/Http/Databases/Tables/Update.php | 6 +----- .../Databases/Http/Databases/Tables/Usage/Get.php | 5 +---- .../Modules/Databases/Http/Databases/Tables/XList.php | 5 +---- 40 files changed, 42 insertions(+), 208 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php index 6f48afe57f..ada1d83424 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php @@ -2,14 +2,11 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Boolean; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as BooleanCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -67,8 +64,6 @@ class Create extends BooleanCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php index 485a9587e3..5316677adc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Boolean; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as BooleanUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -68,8 +66,6 @@ class Update extends BooleanUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php index 1a5f6f65e1..a0731f0767 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Datetime; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as DatetimeCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -68,8 +66,6 @@ class Create extends DatetimeCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php index 1eb13852e7..495e96f025 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Datetime; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Update as DatetimeUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -69,8 +68,6 @@ class Update extends DatetimeUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php index 10604029dd..68cb00e18b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php @@ -2,15 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Delete as AttributesDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -66,8 +63,6 @@ class Delete extends AttributesDelete ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php index 2d7b65931f..569d641118 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php @@ -2,15 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Email; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Network\Validator\Email; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Email\Create as EmailCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -68,8 +65,6 @@ class Create extends EmailCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php index a067334a9d..bb3abb10d3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Email; -use Appwrite\Event\Event; use Appwrite\Network\Validator\Email; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Email\Update as EmailUpdate; use Appwrite\SDK\AuthType; @@ -10,7 +9,6 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -69,8 +67,6 @@ class Update extends EmailUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php index 8fdb689b46..47d06d4a53 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Enum; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Enum\Create as EnumCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -70,8 +68,6 @@ class Create extends EnumCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $elements, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php index 85e1c4e5cc..18c1db0683 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Enum; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Enum\Update as EnumUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -71,8 +70,6 @@ class Update extends EnumUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $elements, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php index 2ad2c11572..217378e274 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php @@ -2,14 +2,11 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Float; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Float\Create as FloatCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -70,8 +67,6 @@ class Create extends FloatCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php index ef6080b86c..be9e2f6b09 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Float; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Float\Update as FloatUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -71,8 +69,6 @@ class Update extends FloatUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php index 42ba23ad3a..630ac01e0f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php @@ -7,7 +7,6 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -67,8 +66,6 @@ class Get extends AttributesGet ->param('key', '', new Key(), 'Column Key.') ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $tableId, $key, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php index 34d5ecdfca..4da949dd74 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php @@ -2,14 +2,11 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\IP; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Create as IPCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -68,8 +65,6 @@ class Create extends IPCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php index 34cfe97343..7adda8a4d5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\IP; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Update as IPUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -69,8 +67,6 @@ class Update extends IPUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php index 13aa90b6e3..2172b5067b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php @@ -2,14 +2,11 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Integer; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Create as IntegerCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -70,8 +67,6 @@ class Create extends IntegerCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php index 6cf3383795..26f7e8625c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Integer; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Update as IntegerUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -71,8 +69,6 @@ class Update extends IntegerUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $min, $max, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index be5da989af..76dd80cec9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Relationship; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Create as RelationshipCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -79,8 +77,6 @@ class Create extends RelationshipCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $relatedTableId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $relatedTableId, $type, $twoWay, $key, $twoWayKey, $onDelete, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php index 1afdd0c56a..b2229bb32d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Relationship; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Update as RelationshipUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -70,8 +69,6 @@ class Update extends RelationshipUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?string $onDelete, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $onDelete, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index e17ac07ccf..cf12e6582d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -2,14 +2,11 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\String; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Create as StringCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -72,8 +69,6 @@ class Create extends StringCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $size, $required, $default, $array, $encrypt, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php index f64d5408eb..968b15bbef 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\String; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Update as StringUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -72,8 +70,6 @@ class Update extends StringUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $size, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php index a0f4e2ae8f..8c9865cde0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php @@ -2,14 +2,11 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\URL; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\URL\Create as URLCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -68,8 +65,6 @@ class Create extends URLCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $array, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php index 623328e249..e461befe0b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\URL; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\URL\Update as URLUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -69,8 +67,6 @@ class Update extends URLUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $required, $default, $newKey, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index a6b43518b9..3dde78441d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -8,7 +8,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Columns; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -56,8 +55,6 @@ class XList extends AttributesXList ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Columns::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $tableId, $queries, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 1f349b95ed..0ca46f66e0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CollectionCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -69,8 +67,6 @@ class Create extends CollectionCreate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $name, $permissions, $documentSecurity, $enabled, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index 338d344660..c24fad28ef 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -2,15 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as CollectionDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -63,8 +60,6 @@ class Delete extends CollectionDelete ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index ffa1da443e..e277db6d3d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -8,7 +8,6 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -56,8 +55,6 @@ class Get extends CollectionGet ->param('tableId', '', new UID(), 'Table ID.') ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, string $tableId, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $tableId, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index 77e31f4115..b8e22cec71 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Create as IndexCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -38,10 +36,10 @@ class Create extends IndexCreate $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) - ->setHttpPath('/v1/databases/:databaseId/tables/:tables/indexes') + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') ->desc('Create index') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tables].indexes.[indexId].create') + ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'index.create') @@ -70,8 +68,6 @@ class Create extends IndexCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $type, $columns, $orders, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index 7a81942e32..ed8c355075 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -2,15 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Delete as IndexDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -69,8 +66,6 @@ class Delete extends IndexDelete ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - parent::action($databaseId, $tableId, $key, $response, $dbForProject, $queueForDatabase, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index a765bfb975..cd689316e6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -8,7 +8,6 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -58,8 +57,6 @@ class Get extends IndexGet ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $tableId, $key, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index afce04fcf9..3d0e7d5139 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -9,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -58,8 +57,6 @@ class XList extends IndexXList ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $tableId, $queries, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 9a6d4c4e8e..967e53539c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -7,14 +7,10 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; -use Appwrite\Utopia\Response as UtopiaResponse; -use MaxMind\Db\Reader; -use Utopia\Database\Database; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Locale\Locale; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -59,8 +55,6 @@ class XList extends CollectionLogXList ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb) { - parent::action($databaseId, $tableId, $queries, $response, $dbForProject, $locale, $geodb); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 1773a4c2ff..d041630894 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Event\Event; -use Appwrite\Event\StatsUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as DocumentCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,7 +10,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -76,8 +73,6 @@ class Create extends DocumentCreate ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback(function (string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - parent::action($databaseId, $rowId, $tableId, $databaseId, $permissions, $response, $dbForProject, $user, $queueForEvents, $queueForStatsUsage); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php index 05a98d6096..e40046bdf9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php @@ -2,15 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Event\Event; -use Appwrite\Event\StatsUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DocumentDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -74,8 +71,6 @@ class Delete extends DocumentDelete ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback(function (string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - parent::action($databaseId, $tableId, $rowId, $requestTimestamp, $response, $dbForProject, $queueForEvents, $queueForStatsUsage); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php index 717cec3721..5ff02e96e8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Event\StatsUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as DocumentGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -62,8 +60,6 @@ class Get extends DocumentGet ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback(function (string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { - parent::action($databaseId, $tableId, $rowId, $queries, $response, $dbForProject, $queueForStatsUsage); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php index 23b70ac19e..29a92413f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php @@ -7,14 +7,10 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; -use Appwrite\Utopia\Response as UtopiaResponse; -use MaxMind\Db\Reader; -use Utopia\Database\Database; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Locale\Locale; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -60,8 +56,6 @@ class XList extends DocumentLogXList ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback(function (string $databaseId, string $tableId, string $rowId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb) { - parent::action($databaseId, $tableId, $rowId, $queries, $response, $dbForProject, $locale, $geodb); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index 0a83feee38..3c98d3c499 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Event\Event; -use Appwrite\Event\StatsUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Update as DocumentUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -72,8 +70,6 @@ class Update extends DocumentUpdate ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback(function (string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { - parent::action($databaseId, $tableId, $rowId, $data, $permissions, $requestTimestamp, $response, $dbForProject, $queueForEvents, $queueForStatsUsage); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php index f4bdbaa483..c74a87c842 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Event\StatsUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\XList as DocumentXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -61,8 +59,6 @@ class XList extends DocumentXList ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback(function (string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { - parent::action($databaseId, $tableId, $queries, $response, $dbForProject, $queueForStatsUsage); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index e60fbb4ac8..85024e4008 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -2,14 +2,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Update as CollectionUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -68,8 +66,6 @@ class Update extends CollectionUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback(function (string $databaseId, string $tableId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents) { - parent::action($databaseId, $tableId, $name, $permissions, $documentSecurity, $enabled, $response, $dbForProject, $queueForEvents); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index 304884cece..e67850e361 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -8,7 +8,6 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -58,8 +57,6 @@ class Get extends CollectionUsageGet ->param('tableId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, string $range, string $tableId, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $range, $tableId, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index 10913cd491..b2f102cc55 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -9,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Tables; use Appwrite\Utopia\Response as UtopiaResponse; -use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; @@ -59,8 +58,6 @@ class XList extends CollectionXList ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') - ->callback(function (string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject) { - parent::action($databaseId, $queries, $search, $response, $dbForProject); - }); + ->callback([$this, 'action']); } } From d3f1c4f8526914bcd63afb26a8ebff010e7457c0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 15:10:44 +0530 Subject: [PATCH 086/173] revert: tests to `collections` API. --- .../Services/Migrations/MigrationsBase.php | 16 +-- tests/e2e/Services/Webhooks/WebhooksBase.php | 104 +++++++++--------- .../Webhooks/WebhooksCustomServerTest.php | 44 ++++---- tests/unit/Messaging/MessagingTest.php | 38 +++---- 4 files changed, 101 insertions(+), 101 deletions(-) diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index a40a2156ea..c241b38e3d 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -445,7 +445,7 @@ trait MigrationsBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Test Collection', ]); @@ -536,7 +536,7 @@ trait MigrationsBase return [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, + 'collectionId' => $collectionId, ]; } @@ -546,14 +546,14 @@ trait MigrationsBase public function testAppwriteMigrationDatabasesDocument(array $data): void { $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Test Document', ] @@ -927,7 +927,7 @@ trait MigrationsBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'Test collection', - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -1131,7 +1131,7 @@ trait MigrationsBase return [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, + 'collectionId' => $collectionId, 'migrationId' => $migration['body']['$id'], ]; } @@ -1142,7 +1142,7 @@ trait MigrationsBase public function testImportSuccessful(array $response): void { $databaseId = $response['databaseId']; - $collectionId = $response['tableId']; + $collectionId = $response['collectionId']; $migrationId = $response['migrationId']; $documentsCountInCSV = 100; @@ -1176,7 +1176,7 @@ trait MigrationsBase ]); $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertIsArray($documents['body']['rows']); + $this->assertIsArray($documents['body']['documents']); $this->assertIsNumeric($documents['body']['total']); $this->assertEquals($documentsCountInCSV, $documents['body']['total']); } diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index feb43d17ab..c743810feb 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -61,7 +61,7 @@ trait WebhooksBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -83,10 +83,10 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -155,12 +155,12 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -181,12 +181,12 @@ trait WebhooksBase // $this->assertEquals($webhook['method'], 'DELETE'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -211,7 +211,7 @@ trait WebhooksBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'firstName' => 'Chris', 'lastName' => 'Evans', @@ -234,16 +234,16 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -254,7 +254,7 @@ trait WebhooksBase $this->assertIsArray($webhook['data']['$permissions']); $this->assertCount(3, $webhook['data']['$permissions']); - $data['rowId'] = $document['body']['$id']; + $data['documentId'] = $document['body']['$id']; return $data; } @@ -270,7 +270,7 @@ trait WebhooksBase /** * Test for SUCCESS */ - $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $data['rowId'], array_merge([ + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $data['documentId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -296,16 +296,16 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -335,7 +335,7 @@ trait WebhooksBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'firstName' => 'Bradly', 'lastName' => 'Cooper', @@ -366,16 +366,16 @@ trait WebhooksBase $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.documents.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.*.documents.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -1119,7 +1119,7 @@ trait WebhooksBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'newCollection' . $i, 'permissions' => [ Permission::read(Role::any()), diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 83a29b741f..d2f132e960 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -48,10 +48,10 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -79,7 +79,7 @@ class WebhooksCustomServerTest extends Scope ]), [ 'key' => 'fullname', 'type' => 'key', - 'columns' => ['lastName', 'firstName'], + 'attributes' => ['lastName', 'firstName'], 'orders' => ['ASC', 'ASC'], ]); @@ -96,12 +96,12 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -121,12 +121,12 @@ class WebhooksCustomServerTest extends Scope // $this->assertEquals($webhook['method'], 'DELETE'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -159,7 +159,7 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Demo', 'permissions' => [ Permission::read(Role::any()), @@ -189,10 +189,10 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['method'], 'POST'); $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); diff --git a/tests/unit/Messaging/MessagingTest.php b/tests/unit/Messaging/MessagingTest.php index 2cb15c5750..c2b6490869 100644 --- a/tests/unit/Messaging/MessagingTest.php +++ b/tests/unit/Messaging/MessagingTest.php @@ -35,7 +35,7 @@ class MessagingTest extends TestCase Role::team(ID::custom('def'))->toString(), Role::team(ID::custom('def'), 'guest')->toString(), ], - ['files' => 0, 'rows' => 0, 'rows.789' => 0, 'account.123' => 0] + ['files' => 0, 'documents' => 0, 'documents.789' => 0, 'account.123' => 0] ); $event = [ @@ -115,13 +115,13 @@ class MessagingTest extends TestCase $this->assertEmpty($receivers); $event['roles'] = [Role::any()->toString()]; - $event['data']['channels'] = ['rows.123']; + $event['data']['channels'] = ['documents.123']; $receivers = $realtime->getSubscribers($event); $this->assertEmpty($receivers); - $event['data']['channels'] = ['rows.789']; + $event['data']['channels'] = ['documents.789']; $receivers = $realtime->getSubscribers($event); @@ -153,8 +153,8 @@ class MessagingTest extends TestCase $channels = [ 0 => 'files', - 1 => 'rows', - 2 => 'rows.789', + 1 => 'documents', + 2 => 'documents.789', 3 => 'account', 4 => 'account.456' ]; @@ -162,8 +162,8 @@ class MessagingTest extends TestCase $channels = Realtime::convertChannels($channels, $user->getId()); $this->assertCount(4, $channels); $this->assertArrayHasKey('files', $channels); - $this->assertArrayHasKey('rows', $channels); - $this->assertArrayHasKey('rows.789', $channels); + $this->assertArrayHasKey('documents', $channels); + $this->assertArrayHasKey('documents.789', $channels); $this->assertArrayHasKey('account', $channels); $this->assertArrayNotHasKey('account.456', $channels); } @@ -190,8 +190,8 @@ class MessagingTest extends TestCase ]); $channels = [ 0 => 'files', - 1 => 'rows', - 2 => 'rows.789', + 1 => 'documents', + 2 => 'documents.789', 3 => 'account', 4 => 'account.456' ]; @@ -200,8 +200,8 @@ class MessagingTest extends TestCase $this->assertCount(5, $channels); $this->assertArrayHasKey('files', $channels); - $this->assertArrayHasKey('rows', $channels); - $this->assertArrayHasKey('rows.789', $channels); + $this->assertArrayHasKey('documents', $channels); + $this->assertArrayHasKey('documents.789', $channels); $this->assertArrayHasKey('account.123', $channels); $this->assertArrayHasKey('account', $channels); $this->assertArrayNotHasKey('account.456', $channels); @@ -213,10 +213,10 @@ class MessagingTest extends TestCase * Test Collection Level Permissions */ $result = Realtime::fromPayload( - event: 'databases.database_id.tables.collection_id.rows.document_id.create', + event: 'databases.database_id.collections.collection_id.documents.document_id.create', payload: new Document([ '$id' => ID::custom('test'), - '$collection' => ID::custom('table'), + '$collection' => ID::custom('collection'), '$permissions' => [ Permission::read(Role::team('123abc')), Permission::update(Role::team('123abc')), @@ -226,8 +226,8 @@ class MessagingTest extends TestCase database: new Document([ '$id' => ID::custom('database'), ]), - table: new Document([ - '$id' => ID::custom('table'), + collection: new Document([ + '$id' => ID::custom('collection'), '$permissions' => [ Permission::read(Role::any()), Permission::update(Role::any()), @@ -243,10 +243,10 @@ class MessagingTest extends TestCase * Test Document Level Permissions */ $result = Realtime::fromPayload( - event: 'databases.database_id.tables.collection_id.rows.document_id.create', + event: 'databases.database_id.collections.collection_id.documents.document_id.create', payload: new Document([ '$id' => ID::custom('test'), - '$collection' => ID::custom('table'), + '$collection' => ID::custom('collection'), '$permissions' => [ Permission::read(Role::any()), Permission::update(Role::any()), @@ -256,8 +256,8 @@ class MessagingTest extends TestCase database: new Document([ '$id' => ID::custom('database'), ]), - table: new Document([ - '$id' => ID::custom('table'), + collection: new Document([ + '$id' => ID::custom('collection'), '$permissions' => [ Permission::read(Role::team('123abc')), Permission::update(Role::team('123abc')), From 94fbf19e546b30b0154eefd7907486d1e301c73b Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 15:11:14 +0530 Subject: [PATCH 087/173] fix|add: tables api tests to realtime. --- .../Realtime/RealtimeConsoleClientTest.php | 398 +++++++++++++++++- .../Realtime/RealtimeCustomClientTest.php | 276 ++++++------ 2 files changed, 513 insertions(+), 161 deletions(-) diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index bb21ef8619..a81baf5aaa 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -123,9 +123,8 @@ class RealtimeConsoleClientTest extends Scope $client->close(); } - public function testAttributes(): array + public function testAttributesCollectionsAPI(): array { - $user = $this->getUser(); $projectId = 'console'; $client = $this->getWebsocket(['console'], [ @@ -162,7 +161,7 @@ class RealtimeConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -184,13 +183,128 @@ class RealtimeConsoleClientTest extends Scope ]); $projectId = $this->getProject()['$id']; - $attributeKey = $name['body']['key']; - $this->assertEquals($name['headers']['status-code'], 202); - $this->assertEquals($name['body']['key'], 'name'); - $this->assertEquals($name['body']['type'], 'string'); - $this->assertEquals($name['body']['size'], 256); - $this->assertEquals($name['body']['required'], true); + $this->assertEquals(202, $name['headers']['status-code']); + $this->assertEquals('name', $name['body']['key']); + $this->assertEquals('string', $name['body']['type']); + $this->assertEquals(256, $name['body']['size']); + $this->assertTrue($name['body']['required']); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}", $response['data']['events']); + $this->assertContains("databases.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + $this->assertEquals('processing', $response['data']['payload']['status']); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}", $response['data']['events']); + $this->assertContains("databases.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + $this->assertEquals('available', $response['data']['payload']['status']); + + $client->close(); + + return ['actorsId' => $actorsId, 'databaseId' => $databaseId]; + } + + public function testAttributesTablesAPI(): array + { + $projectId = 'console'; + + $client = $this->getWebsocket(['console'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ], $projectId); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('connected', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertCount(1, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertNotEmpty($response['data']['user']); + + /** + * Create database + */ + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'databaseId' => ID::unique(), + 'name' => 'Actors DB', + ]); + + $databaseId = $database['body']['$id']; + + /** + * Test Attributes + */ + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'tableId' => ID::unique(), + 'name' => 'Actors', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $actorsId = $actors['body']['$id']; + + $name = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $projectId = $this->getProject()['$id']; + + $this->assertEquals(202, $name['headers']['status-code']); + $this->assertEquals('name', $name['body']['key']); + $this->assertEquals('string', $name['body']['type']); + $this->assertEquals(256, $name['body']['size']); + $this->assertTrue($name['body']['required']); + $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); @@ -235,15 +349,13 @@ class RealtimeConsoleClientTest extends Scope $client->close(); - $data = ['actorsId' => $actorsId, 'databaseId' => $databaseId]; - - return $data; + return ['actorsId' => $actorsId, 'databaseId' => $databaseId]; } /** - * @depends testAttributes + * @depends testAttributesCollectionsAPI */ - public function testIndexes(array $data) + public function testIndexesCollectionAPI(array $data) { $projectId = 'console'; $actorsId = $data['actorsId']; @@ -269,6 +381,90 @@ class RealtimeConsoleClientTest extends Scope $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $actorsId . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'key' => 'key_name', + 'type' => 'key', + 'attributes' => [ + 'name', + ], + ]); + + $this->assertEquals(202, $index['headers']['status-code']); + + $projectId = $this->getProject()['$id']; + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + $this->assertEquals('processing', $response['data']['payload']['status']); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + $this->assertEquals('available', $response['data']['payload']['status']); + + $client->close(); + + return $data; + } + + /** + * @depends testAttributesTablesAPI + */ + public function testIndexesTablesAPI(array $data) + { + $projectId = 'console'; + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + $client = $this->getWebsocket(['console'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ], $projectId); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('connected', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertCount(1, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertNotEmpty($response['data']['user']); + + /** + * Test Indexes + */ + $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'key' => 'key_name', 'type' => 'key', @@ -277,10 +473,9 @@ class RealtimeConsoleClientTest extends Scope ], ]); - $this->assertEquals($index['headers']['status-code'], 202); + $this->assertEquals(202, $index['headers']['status-code']); $projectId = $this->getProject()['$id']; - $indexKey = $index['body']['key']; $response = json_decode($client->receive(), true); @@ -326,9 +521,9 @@ class RealtimeConsoleClientTest extends Scope } /** - * @depends testIndexes + * @depends testIndexesCollectionAPI */ - public function testDeleteIndex(array $data) + public function testDeleteIndexCollectionsAPI(array $data) { $actorsId = $data['actorsId']; $projectId = 'console'; @@ -360,7 +555,88 @@ class RealtimeConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals($attribute['headers']['status-code'], 204); + $this->assertEquals(204, $attribute['headers']['status-code']); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + + /** Delete index generates two events. One from the API and one from the database worker */ + $response = json_decode($client->receive(), true); + + + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + + $client->close(); + + return $data; + } + + /** + * @depends testIndexesTablesAPI + */ + public function testDeleteIndexTablesAPI(array $data) + { + $projectId = 'console'; + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + $client = $this->getWebsocket(['console'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ], $projectId); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('connected', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertCount(1, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertNotEmpty($response['data']['user']); + + $projectId = $this->getProject()['$id']; + + /** + * Test Delete Index + */ + $indexKey = 'key_name'; + $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actorsId . '/indexes/' . $indexKey, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(204, $attribute['headers']['status-code']); $response = json_decode($client->receive(), true); @@ -382,6 +658,7 @@ class RealtimeConsoleClientTest extends Scope /** Delete index generates two events. One from the API and one from the database worker */ $response = json_decode($client->receive(), true); + $this->assertArrayHasKey('type', $response); $this->assertArrayHasKey('data', $response); $this->assertEquals('event', $response['type']); @@ -404,12 +681,12 @@ class RealtimeConsoleClientTest extends Scope } /** - * @depends testDeleteIndex + * @depends testDeleteIndexCollectionsAPI */ - public function testDeleteAttribute(array $data) + public function testDeleteAttributeCollectionsAPI(array $data) { - $actorsId = $data['actorsId']; $projectId = 'console'; + $actorsId = $data['actorsId']; $databaseId = $data['databaseId']; $client = $this->getWebsocket(['console'], [ @@ -438,7 +715,82 @@ class RealtimeConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals($attribute['headers']['status-code'], 204); + $this->assertEquals(204, $attribute['headers']['status-code']); + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(2, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("projects.{$projectId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + + $client->close(); + } + + /** + * @depends testDeleteIndexTablesAPI + */ + public function testDeleteAttributeTablesAPI(array $data) + { + $projectId = 'console'; + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + $client = $this->getWebsocket(['console'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ], $projectId); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('connected', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertCount(1, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertNotEmpty($response['data']['user']); + + $attributeKey = 'name'; + $projectId = $this->getProject()['$id']; + + /** + * Test Delete Attribute + */ + $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['actorsId'] . '/columns/' . $attributeKey, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(204, $attribute['headers']['status-code']); $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index eef7978a0e..dab096c316 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -31,7 +31,7 @@ class RealtimeCustomClientTest extends Scope 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session ]; - $client = $this->getWebsocket(['rows'], $headers); + $client = $this->getWebsocket(['documents'], $headers); $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); @@ -40,7 +40,7 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertNotEmpty($response['data']['user']); $this->assertCount(1, $response['data']['channels']); - $this->assertContains('rows', $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); $client->close(); @@ -60,7 +60,7 @@ class RealtimeCustomClientTest extends Scope $client->close(); - $client = $this->getWebsocket(['account', 'rows', 'account.123'], $headers); + $client = $this->getWebsocket(['account', 'documents', 'account.123'], $headers); $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); @@ -69,7 +69,7 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($response['data']); $this->assertNotEmpty($response['data']['user']); $this->assertCount(3, $response['data']['channels']); - $this->assertContains('rows', $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); $this->assertContains('account', $response['data']['channels']); $this->assertContains('account.' . $userId, $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); @@ -80,12 +80,12 @@ class RealtimeCustomClientTest extends Scope 'account', 'files', 'files.1', - 'tables', - 'tables.1.rows', - 'tables.2.rows', - 'rows', - 'tables.1.rows.1', - 'tables.2.rows.2', + 'collections', + 'collections.1.documents', + 'collections.2.documents', + 'documents', + 'collections.1.documents.1', + 'collections.2.documents.2', ], $headers); $response = json_decode($client->receive(), true); @@ -100,12 +100,12 @@ class RealtimeCustomClientTest extends Scope $this->assertContains('account.' . $userId, $response['data']['channels']); $this->assertContains('files', $response['data']['channels']); $this->assertContains('files.1', $response['data']['channels']); - $this->assertContains('tables', $response['data']['channels']); - $this->assertContains('tables.1.rows', $response['data']['channels']); - $this->assertContains('tables.2.rows', $response['data']['channels']); - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains('tables.1.rows.1', $response['data']['channels']); - $this->assertContains('tables.2.rows.2', $response['data']['channels']); + $this->assertContains('collections', $response['data']['channels']); + $this->assertContains('collections.1.documents', $response['data']['channels']); + $this->assertContains('collections.2.documents', $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains('collections.1.documents.1', $response['data']['channels']); + $this->assertContains('collections.2.documents.2', $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); $client->close(); @@ -245,7 +245,7 @@ class RealtimeCustomClientTest extends Scope /** * Test for FAILURE */ - $client = $this->getWebsocket(['rows'], ['origin' => 'http://appwrite.unknown']); + $client = $this->getWebsocket(['documents'], ['origin' => 'http://appwrite.unknown']); $payload = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $payload); @@ -675,7 +675,7 @@ class RealtimeCustomClientTest extends Scope $session = $user['session'] ?? ''; $projectId = $this->getProject()['$id']; - $client = $this->getWebsocket(['rows', 'tables'], [ + $client = $this->getWebsocket(['documents', 'collections'], [ 'origin' => 'http://localhost', 'cookie' => 'a_session_' . $projectId . '=' . $session ]); @@ -687,8 +687,8 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('connected', $response['type']); $this->assertNotEmpty($response['data']); $this->assertCount(2, $response['data']['channels']); - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains('tables', $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains('collections', $response['data']['channels']); $this->assertNotEmpty($response['data']['user']); $this->assertEquals($user['$id'], $response['data']['user']['$id']); @@ -714,7 +714,7 @@ class RealtimeCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), @@ -734,11 +734,11 @@ class RealtimeCustomClientTest extends Scope 'required' => true, ]); - $this->assertEquals($name['headers']['status-code'], 202); - $this->assertEquals($name['body']['key'], 'name'); - $this->assertEquals($name['body']['type'], 'string'); - $this->assertEquals($name['body']['size'], 256); - $this->assertEquals($name['body']['required'], true); + $this->assertEquals(202, $name['headers']['status-code']); + $this->assertEquals('name', $name['body']['key']); + $this->assertEquals('string', $name['body']['type']); + $this->assertEquals(256, $name['body']['size']); + $this->assertTrue($name['body']['required']); sleep(2); @@ -749,7 +749,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Chris Evans' ], @@ -769,24 +769,24 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(6, $response['data']['channels']); // includes old and new channels - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows.' . $documentId, $response['data']['channels']); - $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); + $this->assertCount(3, $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents.' . $documentId, $response['data']['channels']); + $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['name'], 'Chris Evans'); + $this->assertEquals('Chris Evans', $response['data']['payload']['name']); /** * Test Document Update @@ -795,7 +795,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Chris Evans 2' ], @@ -813,25 +813,25 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(6, $response['data']['channels']); // includes old and new channels - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); + $this->assertCount(3, $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2'); + $this->assertEquals('Chris Evans 2', $response['data']['payload']['name']); /** * Test Document Delete @@ -840,7 +840,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Bradley Cooper' ], @@ -867,24 +867,24 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(6, $response['data']['channels']); // includes old and new channels - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); + $this->assertCount(3, $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper'); + $this->assertEquals('Bradley Cooper', $response['data']['payload']['name']); $client->close(); } @@ -895,7 +895,7 @@ class RealtimeCustomClientTest extends Scope $session = $user['session'] ?? ''; $projectId = $this->getProject()['$id']; - $client = $this->getWebsocket(['rows', 'tables'], [ + $client = $this->getWebsocket(['documents', 'collections'], [ 'origin' => 'http://localhost', 'cookie' => 'a_session_' . $projectId . '=' . $session ]); @@ -907,8 +907,8 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('connected', $response['type']); $this->assertNotEmpty($response['data']); $this->assertCount(2, $response['data']['channels']); - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains('tables', $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains('collections', $response['data']['channels']); $this->assertNotEmpty($response['data']['user']); $this->assertEquals($user['$id'], $response['data']['user']['$id']); @@ -934,7 +934,7 @@ class RealtimeCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'permissions' => [ Permission::read(Role::any()), @@ -956,11 +956,11 @@ class RealtimeCustomClientTest extends Scope 'required' => true, ]); - $this->assertEquals($name['headers']['status-code'], 202); - $this->assertEquals($name['body']['key'], 'name'); - $this->assertEquals($name['body']['type'], 'string'); - $this->assertEquals($name['body']['size'], 256); - $this->assertEquals($name['body']['required'], true); + $this->assertEquals(202, $name['headers']['status-code']); + $this->assertEquals('name', $name['body']['key']); + $this->assertEquals('string', $name['body']['type']); + $this->assertEquals(256, $name['body']['size']); + $this->assertTrue($name['body']['required']); sleep(2); @@ -971,7 +971,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Chris Evans' ], @@ -987,24 +987,24 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(6, $response['data']['channels']); // includes old and new channels - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*.create", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); + $this->assertCount(3, $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*.create", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['name'], 'Chris Evans'); + $this->assertEquals('Chris Evans', $response['data']['payload']['name']); /** * Test Document Update @@ -1026,25 +1026,25 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(6, $response['data']['channels']); // includes old and new channels - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*.update", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); + $this->assertCount(3, $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2'); + $this->assertEquals('Chris Evans 2', $response['data']['payload']['name']); /** * Test Document Delete @@ -1053,7 +1053,7 @@ class RealtimeCustomClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rowId' => ID::unique(), + 'documentId' => ID::unique(), 'data' => [ 'name' => 'Bradley Cooper' ], @@ -1076,24 +1076,24 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(6, $response['data']['channels']); // includes old and new channels - $this->assertContains('rows', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.{$documentId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*.delete", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*.rows.*", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.tables.*", $response['data']['events']); + $this->assertCount(3, $response['data']['channels']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.{$documentId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}", $response['data']['events']); $this->assertContains("databases.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper'); + $this->assertEquals('Bradley Cooper', $response['data']['payload']['name']); $client->close(); } @@ -1293,7 +1293,7 @@ class RealtimeCustomClientTest extends Scope $functionId = $function['body']['$id'] ?? ''; - $this->assertEquals($function['headers']['status-code'], 201); + $this->assertEquals(201, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ @@ -1308,7 +1308,7 @@ class RealtimeCustomClientTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals($deployment['headers']['status-code'], 202); + $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); // Poll until deployment is built @@ -1468,7 +1468,7 @@ class RealtimeCustomClientTest extends Scope 'name' => 'Manchester' ]); - $this->assertEquals($team['headers']['status-code'], 200); + $this->assertEquals(200, $team['headers']['status-code']); $this->assertNotEmpty($team['body']['$id']); $response = json_decode($client->receive(), true); @@ -1500,9 +1500,9 @@ class RealtimeCustomClientTest extends Scope ] ]); - $this->assertEquals($team['headers']['status-code'], 200); - $this->assertEquals($team['body']['funcKey1'], 'funcValue1'); - $this->assertEquals($team['body']['funcKey2'], 'funcValue2'); + $this->assertEquals(200, $team['headers']['status-code']); + $this->assertEquals('funcValue1', $team['body']['funcKey1']); + $this->assertEquals('funcValue2', $team['body']['funcKey2']); $response = json_decode($client->receive(), true); @@ -1521,8 +1521,8 @@ class RealtimeCustomClientTest extends Scope $this->assertContains("teams.*.update", $response['data']['events']); $this->assertContains("teams.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - $this->assertEquals($response['data']['payload']['funcKey1'], 'funcValue1'); - $this->assertEquals($response['data']['payload']['funcKey2'], 'funcValue2'); + $this->assertEquals('funcValue1', $response['data']['payload']['funcKey1']); + $this->assertEquals('funcValue2', $response['data']['payload']['funcKey2']); $client->close(); From 1756acd89cac2301b888da421812710608034607 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 15:16:10 +0530 Subject: [PATCH 088/173] fix: realtime channel count. --- .../Services/Realtime/RealtimeCustomClientTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index dab096c316..1106c74ee0 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -769,7 +769,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents.' . $documentId, $response['data']['channels']); $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents', $response['data']['channels']); @@ -813,7 +813,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); @@ -867,7 +867,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); @@ -987,7 +987,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); @@ -1026,7 +1026,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); @@ -1076,7 +1076,7 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('event', $response['type']); $this->assertNotEmpty($response['data']); $this->assertArrayHasKey('timestamp', $response['data']); - $this->assertCount(3, $response['data']['channels']); + $this->assertCount(6, $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); From f16ba8dee3d8c0e3924b9f13c25ee574d59693d3 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 17:25:36 +0530 Subject: [PATCH 089/173] add: table api tests. --- .github/workflows/tests.yml | 2 + .../Collections/Documents/Create.php | 1 - .../Databases/Tables/DatabasesBase.php | 4919 +++++++++++++++++ .../Tables/DatabasesConsoleClientTest.php | 336 ++ .../Tables/DatabasesCustomClientTest.php | 893 +++ .../Tables/DatabasesCustomServerTest.php | 4113 ++++++++++++++ .../Tables/DatabasesPermissionsGuestTest.php | 278 + .../Tables/DatabasesPermissionsMemberTest.php | 271 + .../Tables/DatabasesPermissionsScope.php | 87 + .../Tables/DatabasesPermissionsTeamTest.php | 208 + 10 files changed, 11107 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesBase.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php create mode 100644 tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4775272185..335e46c16b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -146,6 +146,7 @@ jobs: Avatars, Console, Databases/Collections, + Databases/Tables, Functions, FunctionsSchedule, GraphQL, @@ -214,6 +215,7 @@ jobs: Avatars, Console, Databases/Collections, + Databases/Tables, Functions, FunctionsSchedule, GraphQL, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 4c63837d57..a8f1561f84 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -253,7 +253,6 @@ class Create extends Action throw new Exception($this->getParentNotFoundException()); } - // Add $collectionId and $databaseId for all documents $processDocument = function (Document $table, Document $document) use (&$processDocument, $dbForProject, $database) { $document->setAttribute('$databaseId', $database->getId()); diff --git a/tests/e2e/Services/Databases/Tables/DatabasesBase.php b/tests/e2e/Services/Databases/Tables/DatabasesBase.php new file mode 100644 index 0000000000..5532bbac46 --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesBase.php @@ -0,0 +1,4919 @@ +client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Test Database' + ]); + + $this->assertNotEmpty($database['body']['$id']); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('Test Database', $database['body']['name']); + + return ['databaseId' => $database['body']['$id']]; + } + + /** + * @depends testCreateDatabase + */ + public function testCreateCollection(array $data): array + { + $databaseId = $data['databaseId']; + /** + * Test for SUCCESS + */ + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $movies['headers']['status-code']); + $this->assertEquals($movies['body']['name'], 'Movies'); + + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Actors', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $actors['headers']['status-code']); + $this->assertEquals($actors['body']['name'], 'Actors'); + + return [ + 'databaseId' => $databaseId, + 'moviesId' => $movies['body']['$id'], + 'actorsId' => $actors['body']['$id'], + ]; + } + + /** + * @depends testCreateCollection + */ + public function testConsoleProject(array $data) + { + if ($this->getSide() === 'server') { + // Server side can't get past the invalid key check anyway + $this->expectNotToPerformAssertions(); + return; + } + + $response = $this->client->call( + Client::METHOD_GET, + '/databases/console/tables/' . $data['moviesId'] . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + ], $this->getHeaders()) + ); + + $this->assertEquals(401, $response['headers']['status-code']); + $this->assertEquals('general_access_forbidden', $response['body']['type']); + $this->assertEquals('This endpoint is not available for the console project. The Appwrite Console is a reserved project ID and cannot be used with the Appwrite SDKs and APIs. Please check if your project ID is correct.', $response['body']['message']); + + $response = $this->client->call( + Client::METHOD_GET, + '/databases/console/tables/' . $data['moviesId'] . '/rows', + array_merge([ + 'content-type' => 'application/json', + // 'x-appwrite-project' => '', empty header + ], $this->getHeaders()) + ); + $this->assertEquals(401, $response['headers']['status-code']); + $this->assertEquals('No Appwrite project was specified. Please specify your project ID when initializing your Appwrite SDK.', $response['body']['message']); + } + + /** + * @depends testCreateCollection + */ + public function testDisableCollection(array $data): void + { + $databaseId = $data['databaseId']; + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Movies', + 'enabled' => false, + 'documentSecurity' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertFalse($response['body']['enabled']); + + if ($this->getSide() === 'client') { + $responseCreateDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(404, $responseCreateDocument['headers']['status-code']); + + $responseListDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $responseListDocument['headers']['status-code']); + + $responseGetDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/someID', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $responseGetDocument['headers']['status-code']); + } + + $response = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Movies', + 'enabled' => true, + 'documentSecurity' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertTrue($response['body']['enabled']); + } + + /** + * @depends testCreateCollection + */ + public function testCreateAttributes(array $data): array + { + $databaseId = $data['databaseId']; + + $title = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + $description = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'description', + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $tagline = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'tagline', + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $releaseYear = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'releaseYear', + 'required' => true, + 'min' => 1900, + 'max' => 2200, + ]); + + $duration = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'duration', + 'required' => false, + 'min' => 60, + ]); + + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'actors', + 'size' => 256, + 'required' => false, + 'array' => true, + ]); + + $datetime = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/datetime', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'birthDay', + 'required' => false, + ]); + + $relationship = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $data['actorsId'], + 'type' => 'oneToMany', + 'twoWay' => true, + 'key' => 'starringActors', + 'twoWayKey' => 'movie' + ]); + + $integers = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'integers', + 'required' => false, + 'array' => true, + 'min' => 10, + 'max' => 99, + ]); + + $this->assertEquals(202, $title['headers']['status-code']); + $this->assertEquals($title['body']['key'], 'title'); + $this->assertEquals($title['body']['type'], 'string'); + $this->assertEquals($title['body']['size'], 256); + $this->assertEquals($title['body']['required'], true); + + $this->assertEquals(202, $description['headers']['status-code']); + $this->assertEquals($description['body']['key'], 'description'); + $this->assertEquals($description['body']['type'], 'string'); + $this->assertEquals($description['body']['size'], 512); + $this->assertEquals($description['body']['required'], false); + $this->assertEquals($description['body']['default'], ''); + + $this->assertEquals(202, $tagline['headers']['status-code']); + $this->assertEquals($tagline['body']['key'], 'tagline'); + $this->assertEquals($tagline['body']['type'], 'string'); + $this->assertEquals($tagline['body']['size'], 512); + $this->assertEquals($tagline['body']['required'], false); + $this->assertEquals($tagline['body']['default'], ''); + + $this->assertEquals(202, $releaseYear['headers']['status-code']); + $this->assertEquals($releaseYear['body']['key'], 'releaseYear'); + $this->assertEquals($releaseYear['body']['type'], 'integer'); + $this->assertEquals($releaseYear['body']['required'], true); + + $this->assertEquals(202, $duration['headers']['status-code']); + $this->assertEquals($duration['body']['key'], 'duration'); + $this->assertEquals($duration['body']['type'], 'integer'); + $this->assertEquals($duration['body']['required'], false); + + $this->assertEquals(202, $actors['headers']['status-code']); + $this->assertEquals($actors['body']['key'], 'actors'); + $this->assertEquals($actors['body']['type'], 'string'); + $this->assertEquals($actors['body']['size'], 256); + $this->assertEquals($actors['body']['required'], false); + $this->assertEquals($actors['body']['array'], true); + + $this->assertEquals($datetime['headers']['status-code'], 202); + $this->assertEquals($datetime['body']['key'], 'birthDay'); + $this->assertEquals($datetime['body']['type'], 'datetime'); + $this->assertEquals($datetime['body']['required'], false); + + $this->assertEquals($relationship['headers']['status-code'], 202); + $this->assertEquals($relationship['body']['key'], 'starringActors'); + $this->assertEquals($relationship['body']['type'], 'relationship'); + $this->assertEquals($relationship['body']['relatedTable'], $data['actorsId']); + $this->assertEquals($relationship['body']['relationType'], 'oneToMany'); + $this->assertEquals($relationship['body']['twoWay'], true); + $this->assertEquals($relationship['body']['twoWayKey'], 'movie'); + + $this->assertEquals(202, $integers['headers']['status-code']); + $this->assertEquals($integers['body']['key'], 'integers'); + $this->assertEquals($integers['body']['type'], 'integer'); + $this->assertArrayNotHasKey('size', $integers['body']); + $this->assertEquals($integers['body']['required'], false); + $this->assertEquals($integers['body']['array'], true); + + // wait for database worker to create attributes + sleep(2); + + $movies = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertIsArray($movies['body']['columns']); + $this->assertCount(9, $movies['body']['columns']); + $this->assertEquals($movies['body']['columns'][0]['key'], $title['body']['key']); + $this->assertEquals($movies['body']['columns'][1]['key'], $description['body']['key']); + $this->assertEquals($movies['body']['columns'][2]['key'], $tagline['body']['key']); + $this->assertEquals($movies['body']['columns'][3]['key'], $releaseYear['body']['key']); + $this->assertEquals($movies['body']['columns'][4]['key'], $duration['body']['key']); + $this->assertEquals($movies['body']['columns'][5]['key'], $actors['body']['key']); + $this->assertEquals($movies['body']['columns'][6]['key'], $datetime['body']['key']); + $this->assertEquals($movies['body']['columns'][7]['key'], $relationship['body']['key']); + $this->assertEquals($movies['body']['columns'][8]['key'], $integers['body']['key']); + + return $data; + } + + /** + * @depends testCreateAttributes + */ + public function testListAttributes(array $data): void + { + $databaseId = $data['databaseId']; + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'queries' => [ + Query::equal('type', ['string'])->toString(), + Query::limit(2)->toString(), + Query::cursorAfter(new Document(['$id' => 'title']))->toString() + ], + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, \count($response['body']['columns'])); + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'queries' => [Query::select(['key'])->toString()], + ]); + $this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']); + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * @depends testCreateDatabase + */ + public function testPatchAttribute(array $data): void + { + $databaseId = $data['databaseId']; + + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'patch', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'patch'); + + $attribute = $this->client->call(Client::METHOD_POST, '/databases/'.$databaseId.'/tables/'.$table['body']['$id'].'/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'required' => true, + 'size' => 100, + ]); + $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals($attribute['body']['size'], 100); + + sleep(1); + + $index = $this->client->call(Client::METHOD_POST, '/databases/'.$databaseId.'/tables/'.$table['body']['$id'].'/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'titleIndex', + 'type' => 'key', + 'columns' => ['title'], + ]); + $this->assertEquals(202, $index['headers']['status-code']); + + sleep(1); + + /** + * Update attribute size to exceed Index maximum length + */ + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/'.$databaseId.'/tables/'.$table['body']['$id'].'/columns/string/'.$attribute['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'size' => 1000, + 'required' => true, + 'default' => null, + ]); + + $this->assertEquals(400, $attribute['headers']['status-code']); + $this->assertStringContainsString('Index length is longer than the maximum: 76', $attribute['body']['message']); + } + + public function testUpdateAttributeEnum(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Test Database 2' + ]); + + $players = $this->client->call(Client::METHOD_POST, '/databases/' . $database['body']['$id'] . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Players', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + // Create enum attribute + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $database['body']['$id'] . '/tables/' . $players['body']['$id'] . '/columns/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'position', + 'elements' => ['goalkeeper', 'defender', 'midfielder', 'forward'], + 'required' => true, + 'array' => false, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals($attribute['body']['key'], 'position'); + $this->assertEquals($attribute['body']['elements'], ['goalkeeper', 'defender', 'midfielder', 'forward']); + + \sleep(2); + + // Update enum attribute + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $database['body']['$id'] . '/tables/' . $players['body']['$id'] . '/columns/enum/' . $attribute['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'elements' => ['goalkeeper', 'defender', 'midfielder', 'forward', 'coach'], + 'required' => true, + 'default' => null + ]); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals($attribute['body']['elements'], ['goalkeeper', 'defender', 'midfielder', 'forward', 'coach']); + } + + /** + * @depends testCreateAttributes + */ + public function testAttributeResponseModels(array $data): array + { + $databaseId = $data['databaseId']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Response Models', + // 'permissions' missing on purpose to make sure it's optional + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'Response Models'); + + $tableId = $table['body']['$id']; + + $columnsPath = "/databases/" . $databaseId . "/tables/{$tableId}/columns"; + + $string = $this->client->call(Client::METHOD_POST, $columnsPath . '/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'string', + 'size' => 16, + 'required' => false, + 'default' => 'default', + ]); + + $email = $this->client->call(Client::METHOD_POST, $columnsPath . '/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'email', + 'required' => false, + 'default' => 'default@example.com', + ]); + + $enum = $this->client->call(Client::METHOD_POST, $columnsPath . '/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'enum', + 'elements' => ['yes', 'no', 'maybe'], + 'required' => false, + 'default' => 'maybe', + ]); + + $ip = $this->client->call(Client::METHOD_POST, $columnsPath . '/ip', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'ip', + 'required' => false, + 'default' => '192.0.2.0', + ]); + + $url = $this->client->call(Client::METHOD_POST, $columnsPath . '/url', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'url', + 'required' => false, + 'default' => 'http://example.com', + ]); + + $integer = $this->client->call(Client::METHOD_POST, $columnsPath . '/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'integer', + 'required' => false, + 'min' => 1, + 'max' => 5, + 'default' => 3 + ]); + + $float = $this->client->call(Client::METHOD_POST, $columnsPath . '/float', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'float', + 'required' => false, + 'min' => 1.5, + 'max' => 5.5, + 'default' => 3.5 + ]); + + $boolean = $this->client->call(Client::METHOD_POST, $columnsPath . '/boolean', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'boolean', + 'required' => false, + 'default' => true, + ]); + + $datetime = $this->client->call(Client::METHOD_POST, $columnsPath . '/datetime', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'datetime', + 'required' => false, + 'default' => null, + ]); + + $relationship = $this->client->call(Client::METHOD_POST, $columnsPath . '/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $data['actorsId'], + 'type' => 'oneToMany', + 'twoWay' => true, + 'key' => 'relationship', + 'twoWayKey' => 'twoWayKey' + ]); + + $strings = $this->client->call(Client::METHOD_POST, $columnsPath . '/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'names', + 'size' => 512, + 'required' => false, + 'array' => true, + ]); + + $integers = $this->client->call(Client::METHOD_POST, $columnsPath . '/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'numbers', + 'required' => false, + 'array' => true, + 'min' => 1, + 'max' => 999, + ]); + + $this->assertEquals(202, $string['headers']['status-code']); + $this->assertEquals('string', $string['body']['key']); + $this->assertEquals('string', $string['body']['type']); + $this->assertEquals(false, $string['body']['required']); + $this->assertEquals(false, $string['body']['array']); + $this->assertEquals(16, $string['body']['size']); + $this->assertEquals('default', $string['body']['default']); + + $this->assertEquals(202, $email['headers']['status-code']); + $this->assertEquals('email', $email['body']['key']); + $this->assertEquals('string', $email['body']['type']); + $this->assertEquals(false, $email['body']['required']); + $this->assertEquals(false, $email['body']['array']); + $this->assertEquals('email', $email['body']['format']); + $this->assertEquals('default@example.com', $email['body']['default']); + + $this->assertEquals(202, $enum['headers']['status-code']); + $this->assertEquals('enum', $enum['body']['key']); + $this->assertEquals('string', $enum['body']['type']); + $this->assertEquals(false, $enum['body']['required']); + $this->assertEquals(false, $enum['body']['array']); + $this->assertEquals('enum', $enum['body']['format']); + $this->assertEquals('maybe', $enum['body']['default']); + $this->assertIsArray($enum['body']['elements']); + $this->assertEquals(['yes', 'no', 'maybe'], $enum['body']['elements']); + + $this->assertEquals(202, $ip['headers']['status-code']); + $this->assertEquals('ip', $ip['body']['key']); + $this->assertEquals('string', $ip['body']['type']); + $this->assertEquals(false, $ip['body']['required']); + $this->assertEquals(false, $ip['body']['array']); + $this->assertEquals('ip', $ip['body']['format']); + $this->assertEquals('192.0.2.0', $ip['body']['default']); + + $this->assertEquals(202, $url['headers']['status-code']); + $this->assertEquals('url', $url['body']['key']); + $this->assertEquals('string', $url['body']['type']); + $this->assertEquals(false, $url['body']['required']); + $this->assertEquals(false, $url['body']['array']); + $this->assertEquals('url', $url['body']['format']); + $this->assertEquals('http://example.com', $url['body']['default']); + + $this->assertEquals(202, $integer['headers']['status-code']); + $this->assertEquals('integer', $integer['body']['key']); + $this->assertEquals('integer', $integer['body']['type']); + $this->assertEquals(false, $integer['body']['required']); + $this->assertEquals(false, $integer['body']['array']); + $this->assertEquals(1, $integer['body']['min']); + $this->assertEquals(5, $integer['body']['max']); + $this->assertEquals(3, $integer['body']['default']); + + $this->assertEquals(202, $float['headers']['status-code']); + $this->assertEquals('float', $float['body']['key']); + $this->assertEquals('double', $float['body']['type']); + $this->assertEquals(false, $float['body']['required']); + $this->assertEquals(false, $float['body']['array']); + $this->assertEquals(1.5, $float['body']['min']); + $this->assertEquals(5.5, $float['body']['max']); + $this->assertEquals(3.5, $float['body']['default']); + + $this->assertEquals(202, $boolean['headers']['status-code']); + $this->assertEquals('boolean', $boolean['body']['key']); + $this->assertEquals('boolean', $boolean['body']['type']); + $this->assertEquals(false, $boolean['body']['required']); + $this->assertEquals(false, $boolean['body']['array']); + $this->assertEquals(true, $boolean['body']['default']); + + $this->assertEquals(202, $datetime['headers']['status-code']); + $this->assertEquals('datetime', $datetime['body']['key']); + $this->assertEquals('datetime', $datetime['body']['type']); + $this->assertEquals(false, $datetime['body']['required']); + $this->assertEquals(false, $datetime['body']['array']); + $this->assertEquals(null, $datetime['body']['default']); + + $this->assertEquals(202, $relationship['headers']['status-code']); + $this->assertEquals('relationship', $relationship['body']['key']); + $this->assertEquals('relationship', $relationship['body']['type']); + $this->assertEquals(false, $relationship['body']['required']); + $this->assertEquals(false, $relationship['body']['array']); + $this->assertEquals($data['actorsId'], $relationship['body']['relatedTable']); + $this->assertEquals('oneToMany', $relationship['body']['relationType']); + $this->assertEquals(true, $relationship['body']['twoWay']); + $this->assertEquals('twoWayKey', $relationship['body']['twoWayKey']); + + $this->assertEquals(202, $strings['headers']['status-code']); + $this->assertEquals('names', $strings['body']['key']); + $this->assertEquals('string', $strings['body']['type']); + $this->assertEquals(false, $strings['body']['required']); + $this->assertEquals(true, $strings['body']['array']); + $this->assertEquals(null, $strings['body']['default']); + + $this->assertEquals(202, $integers['headers']['status-code']); + $this->assertEquals('numbers', $integers['body']['key']); + $this->assertEquals('integer', $integers['body']['type']); + $this->assertEquals(false, $integers['body']['required']); + $this->assertEquals(true, $integers['body']['array']); + $this->assertEquals(1, $integers['body']['min']); + $this->assertEquals(999, $integers['body']['max']); + $this->assertEquals(null, $integers['body']['default']); + + // Wait for database worker to create attributes + sleep(5); + + $stringResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $string['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $emailResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $email['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $enumResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $enum['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $ipResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $ip['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $urlResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $url['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $integerResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $integer['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $floatResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $float['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $booleanResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $boolean['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $datetimeResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $datetime['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $relationshipResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $relationship['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $stringsResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $strings['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $integersResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $integers['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $stringResponse['headers']['status-code']); + $this->assertEquals($string['body']['key'], $stringResponse['body']['key']); + $this->assertEquals($string['body']['type'], $stringResponse['body']['type']); + $this->assertEquals('available', $stringResponse['body']['status']); + $this->assertEquals($string['body']['required'], $stringResponse['body']['required']); + $this->assertEquals($string['body']['array'], $stringResponse['body']['array']); + $this->assertEquals(16, $stringResponse['body']['size']); + $this->assertEquals($string['body']['default'], $stringResponse['body']['default']); + + $this->assertEquals(200, $emailResponse['headers']['status-code']); + $this->assertEquals($email['body']['key'], $emailResponse['body']['key']); + $this->assertEquals($email['body']['type'], $emailResponse['body']['type']); + $this->assertEquals('available', $emailResponse['body']['status']); + $this->assertEquals($email['body']['required'], $emailResponse['body']['required']); + $this->assertEquals($email['body']['array'], $emailResponse['body']['array']); + $this->assertEquals($email['body']['format'], $emailResponse['body']['format']); + $this->assertEquals($email['body']['default'], $emailResponse['body']['default']); + + $this->assertEquals(200, $enumResponse['headers']['status-code']); + $this->assertEquals($enum['body']['key'], $enumResponse['body']['key']); + $this->assertEquals($enum['body']['type'], $enumResponse['body']['type']); + $this->assertEquals('available', $enumResponse['body']['status']); + $this->assertEquals($enum['body']['required'], $enumResponse['body']['required']); + $this->assertEquals($enum['body']['array'], $enumResponse['body']['array']); + $this->assertEquals($enum['body']['format'], $enumResponse['body']['format']); + $this->assertEquals($enum['body']['default'], $enumResponse['body']['default']); + $this->assertEquals($enum['body']['elements'], $enumResponse['body']['elements']); + + $this->assertEquals(200, $ipResponse['headers']['status-code']); + $this->assertEquals($ip['body']['key'], $ipResponse['body']['key']); + $this->assertEquals($ip['body']['type'], $ipResponse['body']['type']); + $this->assertEquals('available', $ipResponse['body']['status']); + $this->assertEquals($ip['body']['required'], $ipResponse['body']['required']); + $this->assertEquals($ip['body']['array'], $ipResponse['body']['array']); + $this->assertEquals($ip['body']['format'], $ipResponse['body']['format']); + $this->assertEquals($ip['body']['default'], $ipResponse['body']['default']); + + $this->assertEquals(200, $urlResponse['headers']['status-code']); + $this->assertEquals($url['body']['key'], $urlResponse['body']['key']); + $this->assertEquals($url['body']['type'], $urlResponse['body']['type']); + $this->assertEquals('available', $urlResponse['body']['status']); + $this->assertEquals($url['body']['required'], $urlResponse['body']['required']); + $this->assertEquals($url['body']['array'], $urlResponse['body']['array']); + $this->assertEquals($url['body']['format'], $urlResponse['body']['format']); + $this->assertEquals($url['body']['default'], $urlResponse['body']['default']); + + $this->assertEquals(200, $integerResponse['headers']['status-code']); + $this->assertEquals($integer['body']['key'], $integerResponse['body']['key']); + $this->assertEquals($integer['body']['type'], $integerResponse['body']['type']); + $this->assertEquals('available', $integerResponse['body']['status']); + $this->assertEquals($integer['body']['required'], $integerResponse['body']['required']); + $this->assertEquals($integer['body']['array'], $integerResponse['body']['array']); + $this->assertEquals($integer['body']['min'], $integerResponse['body']['min']); + $this->assertEquals($integer['body']['max'], $integerResponse['body']['max']); + $this->assertEquals($integer['body']['default'], $integerResponse['body']['default']); + + $this->assertEquals(200, $floatResponse['headers']['status-code']); + $this->assertEquals($float['body']['key'], $floatResponse['body']['key']); + $this->assertEquals($float['body']['type'], $floatResponse['body']['type']); + $this->assertEquals('available', $floatResponse['body']['status']); + $this->assertEquals($float['body']['required'], $floatResponse['body']['required']); + $this->assertEquals($float['body']['array'], $floatResponse['body']['array']); + $this->assertEquals($float['body']['min'], $floatResponse['body']['min']); + $this->assertEquals($float['body']['max'], $floatResponse['body']['max']); + $this->assertEquals($float['body']['default'], $floatResponse['body']['default']); + + $this->assertEquals(200, $booleanResponse['headers']['status-code']); + $this->assertEquals($boolean['body']['key'], $booleanResponse['body']['key']); + $this->assertEquals($boolean['body']['type'], $booleanResponse['body']['type']); + $this->assertEquals('available', $booleanResponse['body']['status']); + $this->assertEquals($boolean['body']['required'], $booleanResponse['body']['required']); + $this->assertEquals($boolean['body']['array'], $booleanResponse['body']['array']); + $this->assertEquals($boolean['body']['default'], $booleanResponse['body']['default']); + + $this->assertEquals(200, $datetimeResponse['headers']['status-code']); + $this->assertEquals($datetime['body']['key'], $datetimeResponse['body']['key']); + $this->assertEquals($datetime['body']['type'], $datetimeResponse['body']['type']); + $this->assertEquals('available', $datetimeResponse['body']['status']); + $this->assertEquals($datetime['body']['required'], $datetimeResponse['body']['required']); + $this->assertEquals($datetime['body']['array'], $datetimeResponse['body']['array']); + $this->assertEquals($datetime['body']['default'], $datetimeResponse['body']['default']); + + $this->assertEquals(200, $relationshipResponse['headers']['status-code']); + $this->assertEquals($relationship['body']['key'], $relationshipResponse['body']['key']); + $this->assertEquals($relationship['body']['type'], $relationshipResponse['body']['type']); + $this->assertEquals('available', $relationshipResponse['body']['status']); + $this->assertEquals($relationship['body']['required'], $relationshipResponse['body']['required']); + $this->assertEquals($relationship['body']['array'], $relationshipResponse['body']['array']); + $this->assertEquals($relationship['body']['relatedTable'], $relationshipResponse['body']['relatedTable']); + $this->assertEquals($relationship['body']['relationType'], $relationshipResponse['body']['relationType']); + $this->assertEquals($relationship['body']['twoWay'], $relationshipResponse['body']['twoWay']); + $this->assertEquals($relationship['body']['twoWayKey'], $relationshipResponse['body']['twoWayKey']); + + $columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $columns['headers']['status-code']); + $this->assertEquals(12, $columns['body']['total']); + + $columns = $columns['body']['columns']; + $this->assertIsArray($columns); + $this->assertCount(12, $columns); + + $this->assertEquals($stringResponse['body']['key'], $columns[0]['key']); + $this->assertEquals($stringResponse['body']['type'], $columns[0]['type']); + $this->assertEquals($stringResponse['body']['status'], $columns[0]['status']); + $this->assertEquals($stringResponse['body']['required'], $columns[0]['required']); + $this->assertEquals($stringResponse['body']['array'], $columns[0]['array']); + $this->assertEquals($stringResponse['body']['size'], $columns[0]['size']); + $this->assertEquals($stringResponse['body']['default'], $columns[0]['default']); + + $this->assertEquals($emailResponse['body']['key'], $columns[1]['key']); + $this->assertEquals($emailResponse['body']['type'], $columns[1]['type']); + $this->assertEquals($emailResponse['body']['status'], $columns[1]['status']); + $this->assertEquals($emailResponse['body']['required'], $columns[1]['required']); + $this->assertEquals($emailResponse['body']['array'], $columns[1]['array']); + $this->assertEquals($emailResponse['body']['default'], $columns[1]['default']); + $this->assertEquals($emailResponse['body']['format'], $columns[1]['format']); + + $this->assertEquals($enumResponse['body']['key'], $columns[2]['key']); + $this->assertEquals($enumResponse['body']['type'], $columns[2]['type']); + $this->assertEquals($enumResponse['body']['status'], $columns[2]['status']); + $this->assertEquals($enumResponse['body']['required'], $columns[2]['required']); + $this->assertEquals($enumResponse['body']['array'], $columns[2]['array']); + $this->assertEquals($enumResponse['body']['default'], $columns[2]['default']); + $this->assertEquals($enumResponse['body']['format'], $columns[2]['format']); + $this->assertEquals($enumResponse['body']['elements'], $columns[2]['elements']); + + $this->assertEquals($ipResponse['body']['key'], $columns[3]['key']); + $this->assertEquals($ipResponse['body']['type'], $columns[3]['type']); + $this->assertEquals($ipResponse['body']['status'], $columns[3]['status']); + $this->assertEquals($ipResponse['body']['required'], $columns[3]['required']); + $this->assertEquals($ipResponse['body']['array'], $columns[3]['array']); + $this->assertEquals($ipResponse['body']['default'], $columns[3]['default']); + $this->assertEquals($ipResponse['body']['format'], $columns[3]['format']); + + $this->assertEquals($urlResponse['body']['key'], $columns[4]['key']); + $this->assertEquals($urlResponse['body']['type'], $columns[4]['type']); + $this->assertEquals($urlResponse['body']['status'], $columns[4]['status']); + $this->assertEquals($urlResponse['body']['required'], $columns[4]['required']); + $this->assertEquals($urlResponse['body']['array'], $columns[4]['array']); + $this->assertEquals($urlResponse['body']['default'], $columns[4]['default']); + $this->assertEquals($urlResponse['body']['format'], $columns[4]['format']); + + $this->assertEquals($integerResponse['body']['key'], $columns[5]['key']); + $this->assertEquals($integerResponse['body']['type'], $columns[5]['type']); + $this->assertEquals($integerResponse['body']['status'], $columns[5]['status']); + $this->assertEquals($integerResponse['body']['required'], $columns[5]['required']); + $this->assertEquals($integerResponse['body']['array'], $columns[5]['array']); + $this->assertEquals($integerResponse['body']['default'], $columns[5]['default']); + $this->assertEquals($integerResponse['body']['min'], $columns[5]['min']); + $this->assertEquals($integerResponse['body']['max'], $columns[5]['max']); + + $this->assertEquals($floatResponse['body']['key'], $columns[6]['key']); + $this->assertEquals($floatResponse['body']['type'], $columns[6]['type']); + $this->assertEquals($floatResponse['body']['status'], $columns[6]['status']); + $this->assertEquals($floatResponse['body']['required'], $columns[6]['required']); + $this->assertEquals($floatResponse['body']['array'], $columns[6]['array']); + $this->assertEquals($floatResponse['body']['default'], $columns[6]['default']); + $this->assertEquals($floatResponse['body']['min'], $columns[6]['min']); + $this->assertEquals($floatResponse['body']['max'], $columns[6]['max']); + + $this->assertEquals($booleanResponse['body']['key'], $columns[7]['key']); + $this->assertEquals($booleanResponse['body']['type'], $columns[7]['type']); + $this->assertEquals($booleanResponse['body']['status'], $columns[7]['status']); + $this->assertEquals($booleanResponse['body']['required'], $columns[7]['required']); + $this->assertEquals($booleanResponse['body']['array'], $columns[7]['array']); + $this->assertEquals($booleanResponse['body']['default'], $columns[7]['default']); + + $this->assertEquals($datetimeResponse['body']['key'], $columns[8]['key']); + $this->assertEquals($datetimeResponse['body']['type'], $columns[8]['type']); + $this->assertEquals($datetimeResponse['body']['status'], $columns[8]['status']); + $this->assertEquals($datetimeResponse['body']['required'], $columns[8]['required']); + $this->assertEquals($datetimeResponse['body']['array'], $columns[8]['array']); + $this->assertEquals($datetimeResponse['body']['default'], $columns[8]['default']); + + $this->assertEquals($relationshipResponse['body']['key'], $columns[9]['key']); + $this->assertEquals($relationshipResponse['body']['type'], $columns[9]['type']); + $this->assertEquals($relationshipResponse['body']['status'], $columns[9]['status']); + $this->assertEquals($relationshipResponse['body']['required'], $columns[9]['required']); + $this->assertEquals($relationshipResponse['body']['array'], $columns[9]['array']); + $this->assertEquals($relationshipResponse['body']['relatedTable'], $columns[9]['relatedTable']); + $this->assertEquals($relationshipResponse['body']['relationType'], $columns[9]['relationType']); + $this->assertEquals($relationshipResponse['body']['twoWay'], $columns[9]['twoWay']); + $this->assertEquals($relationshipResponse['body']['twoWayKey'], $columns[9]['twoWayKey']); + + $this->assertEquals($stringsResponse['body']['key'], $columns[10]['key']); + $this->assertEquals($stringsResponse['body']['type'], $columns[10]['type']); + $this->assertEquals($stringsResponse['body']['status'], $columns[10]['status']); + $this->assertEquals($stringsResponse['body']['required'], $columns[10]['required']); + $this->assertEquals($stringsResponse['body']['array'], $columns[10]['array']); + $this->assertEquals($stringsResponse['body']['default'], $columns[10]['default']); + + $this->assertEquals($integersResponse['body']['key'], $columns[11]['key']); + $this->assertEquals($integersResponse['body']['type'], $columns[11]['type']); + $this->assertEquals($integersResponse['body']['status'], $columns[11]['status']); + $this->assertEquals($integersResponse['body']['required'], $columns[11]['required']); + $this->assertEquals($integersResponse['body']['array'], $columns[11]['array']); + $this->assertEquals($integersResponse['body']['default'], $columns[11]['default']); + $this->assertEquals($integersResponse['body']['min'], $columns[11]['min']); + $this->assertEquals($integersResponse['body']['max'], $columns[11]['max']); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $table['headers']['status-code']); + + $columns = $table['body']['columns']; + + $this->assertIsArray($columns); + $this->assertCount(12, $columns); + + $this->assertEquals($stringResponse['body']['key'], $columns[0]['key']); + $this->assertEquals($stringResponse['body']['type'], $columns[0]['type']); + $this->assertEquals($stringResponse['body']['status'], $columns[0]['status']); + $this->assertEquals($stringResponse['body']['required'], $columns[0]['required']); + $this->assertEquals($stringResponse['body']['array'], $columns[0]['array']); + $this->assertEquals($stringResponse['body']['size'], $columns[0]['size']); + $this->assertEquals($stringResponse['body']['default'], $columns[0]['default']); + + $this->assertEquals($emailResponse['body']['key'], $columns[1]['key']); + $this->assertEquals($emailResponse['body']['type'], $columns[1]['type']); + $this->assertEquals($emailResponse['body']['status'], $columns[1]['status']); + $this->assertEquals($emailResponse['body']['required'], $columns[1]['required']); + $this->assertEquals($emailResponse['body']['array'], $columns[1]['array']); + $this->assertEquals($emailResponse['body']['default'], $columns[1]['default']); + $this->assertEquals($emailResponse['body']['format'], $columns[1]['format']); + + $this->assertEquals($enumResponse['body']['key'], $columns[2]['key']); + $this->assertEquals($enumResponse['body']['type'], $columns[2]['type']); + $this->assertEquals($enumResponse['body']['status'], $columns[2]['status']); + $this->assertEquals($enumResponse['body']['required'], $columns[2]['required']); + $this->assertEquals($enumResponse['body']['array'], $columns[2]['array']); + $this->assertEquals($enumResponse['body']['default'], $columns[2]['default']); + $this->assertEquals($enumResponse['body']['format'], $columns[2]['format']); + $this->assertEquals($enumResponse['body']['elements'], $columns[2]['elements']); + + $this->assertEquals($ipResponse['body']['key'], $columns[3]['key']); + $this->assertEquals($ipResponse['body']['type'], $columns[3]['type']); + $this->assertEquals($ipResponse['body']['status'], $columns[3]['status']); + $this->assertEquals($ipResponse['body']['required'], $columns[3]['required']); + $this->assertEquals($ipResponse['body']['array'], $columns[3]['array']); + $this->assertEquals($ipResponse['body']['default'], $columns[3]['default']); + $this->assertEquals($ipResponse['body']['format'], $columns[3]['format']); + + $this->assertEquals($urlResponse['body']['key'], $columns[4]['key']); + $this->assertEquals($urlResponse['body']['type'], $columns[4]['type']); + $this->assertEquals($urlResponse['body']['status'], $columns[4]['status']); + $this->assertEquals($urlResponse['body']['required'], $columns[4]['required']); + $this->assertEquals($urlResponse['body']['array'], $columns[4]['array']); + $this->assertEquals($urlResponse['body']['default'], $columns[4]['default']); + $this->assertEquals($urlResponse['body']['format'], $columns[4]['format']); + + $this->assertEquals($integerResponse['body']['key'], $columns[5]['key']); + $this->assertEquals($integerResponse['body']['type'], $columns[5]['type']); + $this->assertEquals($integerResponse['body']['status'], $columns[5]['status']); + $this->assertEquals($integerResponse['body']['required'], $columns[5]['required']); + $this->assertEquals($integerResponse['body']['array'], $columns[5]['array']); + $this->assertEquals($integerResponse['body']['default'], $columns[5]['default']); + $this->assertEquals($integerResponse['body']['min'], $columns[5]['min']); + $this->assertEquals($integerResponse['body']['max'], $columns[5]['max']); + + $this->assertEquals($floatResponse['body']['key'], $columns[6]['key']); + $this->assertEquals($floatResponse['body']['type'], $columns[6]['type']); + $this->assertEquals($floatResponse['body']['status'], $columns[6]['status']); + $this->assertEquals($floatResponse['body']['required'], $columns[6]['required']); + $this->assertEquals($floatResponse['body']['array'], $columns[6]['array']); + $this->assertEquals($floatResponse['body']['default'], $columns[6]['default']); + $this->assertEquals($floatResponse['body']['min'], $columns[6]['min']); + $this->assertEquals($floatResponse['body']['max'], $columns[6]['max']); + + $this->assertEquals($booleanResponse['body']['key'], $columns[7]['key']); + $this->assertEquals($booleanResponse['body']['type'], $columns[7]['type']); + $this->assertEquals($booleanResponse['body']['status'], $columns[7]['status']); + $this->assertEquals($booleanResponse['body']['required'], $columns[7]['required']); + $this->assertEquals($booleanResponse['body']['array'], $columns[7]['array']); + $this->assertEquals($booleanResponse['body']['default'], $columns[7]['default']); + + $this->assertEquals($datetimeResponse['body']['key'], $columns[8]['key']); + $this->assertEquals($datetimeResponse['body']['type'], $columns[8]['type']); + $this->assertEquals($datetimeResponse['body']['status'], $columns[8]['status']); + $this->assertEquals($datetimeResponse['body']['required'], $columns[8]['required']); + $this->assertEquals($datetimeResponse['body']['array'], $columns[8]['array']); + $this->assertEquals($datetimeResponse['body']['default'], $columns[8]['default']); + + $this->assertEquals($relationshipResponse['body']['key'], $columns[9]['key']); + $this->assertEquals($relationshipResponse['body']['type'], $columns[9]['type']); + $this->assertEquals($relationshipResponse['body']['status'], $columns[9]['status']); + $this->assertEquals($relationshipResponse['body']['required'], $columns[9]['required']); + $this->assertEquals($relationshipResponse['body']['array'], $columns[9]['array']); + $this->assertEquals($relationshipResponse['body']['relatedTable'], $columns[9]['relatedTable']); + $this->assertEquals($relationshipResponse['body']['relationType'], $columns[9]['relationType']); + $this->assertEquals($relationshipResponse['body']['twoWay'], $columns[9]['twoWay']); + $this->assertEquals($relationshipResponse['body']['twoWayKey'], $columns[9]['twoWayKey']); + + $this->assertEquals($stringsResponse['body']['key'], $columns[10]['key']); + $this->assertEquals($stringsResponse['body']['type'], $columns[10]['type']); + $this->assertEquals($stringsResponse['body']['status'], $columns[10]['status']); + $this->assertEquals($stringsResponse['body']['required'], $columns[10]['required']); + $this->assertEquals($stringsResponse['body']['array'], $columns[10]['array']); + $this->assertEquals($stringsResponse['body']['default'], $columns[10]['default']); + + $this->assertEquals($integersResponse['body']['key'], $columns[11]['key']); + $this->assertEquals($integersResponse['body']['type'], $columns[11]['type']); + $this->assertEquals($integersResponse['body']['status'], $columns[11]['status']); + $this->assertEquals($integersResponse['body']['required'], $columns[11]['required']); + $this->assertEquals($integersResponse['body']['array'], $columns[11]['array']); + $this->assertEquals($integersResponse['body']['default'], $columns[11]['default']); + $this->assertEquals($integersResponse['body']['min'], $columns[11]['min']); + $this->assertEquals($integersResponse['body']['max'], $columns[11]['max']); + + /** + * Test for FAILURE + */ + $badEnum = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'enum', + 'elements' => ['yes', 'no', ''], + 'required' => false, + 'default' => 'maybe', + ]); + + $this->assertEquals(400, $badEnum['headers']['status-code']); + $this->assertEquals('Invalid `elements` param: Value must a valid array no longer than 100 items and Value must be a valid string and at least 1 chars and no longer than 255 chars', $badEnum['body']['message']); + + return $data; + } + + /** + * @depends testCreateAttributes + */ + public function testCreateIndexes(array $data): array + { + $databaseId = $data['databaseId']; + + $titleIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'titleIndex', + 'type' => 'fulltext', + 'columns' => ['title'], + ]); + + $this->assertEquals(202, $titleIndex['headers']['status-code']); + $this->assertEquals('titleIndex', $titleIndex['body']['key']); + $this->assertEquals('fulltext', $titleIndex['body']['type']); + $this->assertCount(1, $titleIndex['body']['columns']); + $this->assertEquals('title', $titleIndex['body']['columns'][0]); + + $releaseYearIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'releaseYear', + 'type' => 'key', + 'columns' => ['releaseYear'], + ]); + + $this->assertEquals(202, $releaseYearIndex['headers']['status-code']); + $this->assertEquals('releaseYear', $releaseYearIndex['body']['key']); + $this->assertEquals('key', $releaseYearIndex['body']['type']); + $this->assertCount(1, $releaseYearIndex['body']['columns']); + $this->assertEquals('releaseYear', $releaseYearIndex['body']['columns'][0]); + + $releaseWithDate1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'releaseYearDated', + 'type' => 'key', + 'columns' => ['releaseYear', '$createdAt', '$updatedAt'], + ]); + + $this->assertEquals(202, $releaseWithDate1['headers']['status-code']); + $this->assertEquals('releaseYearDated', $releaseWithDate1['body']['key']); + $this->assertEquals('key', $releaseWithDate1['body']['type']); + $this->assertCount(3, $releaseWithDate1['body']['columns']); + $this->assertEquals('releaseYear', $releaseWithDate1['body']['columns'][0]); + $this->assertEquals('$createdAt', $releaseWithDate1['body']['columns'][1]); + $this->assertEquals('$updatedAt', $releaseWithDate1['body']['columns'][2]); + + $releaseWithDate2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'birthDay', + 'type' => 'key', + 'columns' => ['birthDay'], + ]); + + $this->assertEquals(202, $releaseWithDate2['headers']['status-code']); + $this->assertEquals('birthDay', $releaseWithDate2['body']['key']); + $this->assertEquals('key', $releaseWithDate2['body']['type']); + $this->assertCount(1, $releaseWithDate2['body']['columns']); + $this->assertEquals('birthDay', $releaseWithDate2['body']['columns'][0]); + + // Test for failure + $fulltextReleaseYear = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'releaseYearDated', + 'type' => 'fulltext', + 'columns' => ['releaseYear'], + ]); + + $this->assertEquals(400, $fulltextReleaseYear['headers']['status-code']); + $this->assertEquals($fulltextReleaseYear['body']['message'], 'Attribute "releaseYear" cannot be part of a FULLTEXT index, must be of type string'); + + $noAttributes = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'none', + 'type' => 'key', + 'columns' => [], + ]); + + $this->assertEquals(400, $noAttributes['headers']['status-code']); + $this->assertEquals($noAttributes['body']['message'], 'No attributes provided for index'); + + $duplicates = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'duplicate', + 'type' => 'fulltext', + 'columns' => ['releaseYear', 'releaseYear'], + ]); + + $this->assertEquals(400, $duplicates['headers']['status-code']); + $this->assertEquals($duplicates['body']['message'], 'Duplicate attributes provided'); + + $tooLong = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'tooLong', + 'type' => 'key', + 'columns' => ['description', 'tagline'], + ]); + + $this->assertEquals(400, $tooLong['headers']['status-code']); + $this->assertStringContainsString('Index length is longer than the maximum', $tooLong['body']['message']); + + $fulltextArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'ft', + 'type' => 'fulltext', + 'columns' => ['actors'], + ]); + + $this->assertEquals(400, $fulltextArray['headers']['status-code']); + $this->assertEquals('"Fulltext" index is forbidden on array attributes', $fulltextArray['body']['message']); + + $actorsArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'index-actors', + 'type' => 'key', + 'columns' => ['actors'], + ]); + + $this->assertEquals(202, $actorsArray['headers']['status-code']); + + $twoLevelsArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'index-ip-actors', + 'type' => 'key', + 'columns' => ['releaseYear', 'actors'], // 2 levels + 'orders' => ['DESC', 'DESC'], + ]); + + $this->assertEquals(202, $twoLevelsArray['headers']['status-code']); + $this->assertEquals('DESC', $twoLevelsArray['body']['orders'][0]); + $this->assertEquals(null, $twoLevelsArray['body']['orders'][1]); // Overwrite by API (array) + + $unknown = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'index-unknown', + 'type' => 'key', + 'columns' => ['Unknown'], + ]); + + $this->assertEquals(400, $unknown['headers']['status-code']); + $this->assertEquals('Unknown column: Unknown. Verify the column name or create the column.', $unknown['body']['message']); + + $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'integers-order', + 'type' => 'key', + 'columns' => ['integers'], // array attribute + 'orders' => ['DESC'], // Check order is removed in API + ]); + $this->assertEquals(202, $index1['headers']['status-code']); + + $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'integers-size', + 'type' => 'key', + 'columns' => ['integers'], // array attribute + ]); + $this->assertEquals(202, $index2['headers']['status-code']); + + /** + * Create Indexes by worker + */ + sleep(2); + + $movies = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $this->assertIsArray($movies['body']['indexes']); + $this->assertCount(8, $movies['body']['indexes']); + $this->assertEquals($titleIndex['body']['key'], $movies['body']['indexes'][0]['key']); + $this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']); + $this->assertEquals($releaseWithDate1['body']['key'], $movies['body']['indexes'][2]['key']); + $this->assertEquals($releaseWithDate2['body']['key'], $movies['body']['indexes'][3]['key']); + foreach ($movies['body']['indexes'] as $index) { + $this->assertEquals('available', $index['status']); + } + + return $data; + } + + /** + * @depends testCreateIndexes + */ + public function testListIndexes(array $data): void + { + $databaseId = $data['databaseId']; + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'queries' => [ + Query::equal('type', ['key'])->toString(), + Query::limit(2)->toString() + ], + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, \count($response['body']['indexes'])); + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'queries' => [ + Query::select(['key'])->toString(), + ], + ]); + $this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']); + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * @depends testCreateIndexes + */ + public function testCreateDocument(array $data): array + { + $databaseId = $data['databaseId']; + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + 'releaseYear' => 1944, + 'birthDay' => '1975-06-12 14:12:55+02:00', + 'actors' => [ + 'Chris Evans', + 'Samuel Jackson', + ] + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $row2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Spider-Man: Far From Home', + 'releaseYear' => 2019, + 'birthDay' => null, + 'actors' => [ + 'Tom Holland', + 'Zendaya Maree Stoermer', + 'Samuel Jackson', + ], + 'integers' => [50,60] + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $row3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Spider-Man: Homecoming', + 'releaseYear' => 2017, + 'birthDay' => '1975-06-12 14:12:55 America/New_York', + 'duration' => 65, + 'actors' => [ + 'Tom Holland', + 'Zendaya Maree Stoermer', + ], + 'integers' => [50] + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $row4 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'releaseYear' => 2020, // Missing title, expect an 400 error + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertEquals(201, $row1['headers']['status-code']); + $this->assertEquals($data['moviesId'], $row1['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $row1['body']); + $this->assertEquals($databaseId, $row1['body']['$databaseId']); + $this->assertEquals($row1['body']['title'], 'Captain America'); + $this->assertEquals($row1['body']['releaseYear'], 1944); + $this->assertIsArray($row1['body']['$permissions']); + $this->assertCount(3, $row1['body']['$permissions']); + $this->assertCount(2, $row1['body']['actors']); + $this->assertEquals($row1['body']['actors'][0], 'Chris Evans'); + $this->assertEquals($row1['body']['actors'][1], 'Samuel Jackson'); + $this->assertEquals($row1['body']['birthDay'], '1975-06-12T12:12:55.000+00:00'); + + $this->assertEquals(201, $row2['headers']['status-code']); + $this->assertEquals($data['moviesId'], $row2['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $row2['body']); + $this->assertEquals($databaseId, $row2['body']['$databaseId']); + $this->assertEquals($row2['body']['title'], 'Spider-Man: Far From Home'); + $this->assertEquals($row2['body']['releaseYear'], 2019); + $this->assertEquals($row2['body']['duration'], null); + $this->assertIsArray($row2['body']['$permissions']); + $this->assertCount(3, $row2['body']['$permissions']); + $this->assertCount(3, $row2['body']['actors']); + $this->assertEquals($row2['body']['actors'][0], 'Tom Holland'); + $this->assertEquals($row2['body']['actors'][1], 'Zendaya Maree Stoermer'); + $this->assertEquals($row2['body']['actors'][2], 'Samuel Jackson'); + $this->assertEquals($row2['body']['birthDay'], null); + $this->assertEquals($row2['body']['integers'][0], 50); + $this->assertEquals($row2['body']['integers'][1], 60); + + $this->assertEquals(201, $row3['headers']['status-code']); + $this->assertEquals($data['moviesId'], $row3['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $row3['body']); + $this->assertEquals($databaseId, $row3['body']['$databaseId']); + $this->assertEquals($row3['body']['title'], 'Spider-Man: Homecoming'); + $this->assertEquals($row3['body']['releaseYear'], 2017); + $this->assertEquals($row3['body']['duration'], 65); + $this->assertIsArray($row3['body']['$permissions']); + $this->assertCount(3, $row3['body']['$permissions']); + $this->assertCount(2, $row3['body']['actors']); + $this->assertEquals($row3['body']['actors'][0], 'Tom Holland'); + $this->assertEquals($row3['body']['actors'][1], 'Zendaya Maree Stoermer'); + $this->assertEquals($row3['body']['birthDay'], '1975-06-12T18:12:55.000+00:00'); // UTC for NY + + $this->assertEquals(400, $row4['headers']['status-code']); + + // Delete document 4 with incomplete path + $this->assertEquals(404, $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()))['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateDocument + */ + public function testListDocuments(array $data): array + { + $databaseId = $data['databaseId']; + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('releaseYear')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $rows['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $rows['body']['rows'][2]['releaseYear']); + $this->assertFalse(array_key_exists('$internalId', $rows['body']['rows'][0])); + $this->assertFalse(array_key_exists('$internalId', $rows['body']['rows'][1])); + $this->assertFalse(array_key_exists('$internalId', $rows['body']['rows'][2])); + $this->assertCount(3, $rows['body']['rows']); + + foreach ($rows['body']['rows'] as $row) { + $this->assertArrayNotHasKey('$table', $row); + $this->assertEquals($databaseId, $row['$databaseId']); + $this->assertEquals($data['moviesId'], $row['$tableId']); + } + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderDesc('releaseYear')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1944, $rows['body']['rows'][2]['releaseYear']); + $this->assertEquals(2017, $rows['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $rows['body']['rows'][0]['releaseYear']); + $this->assertCount(3, $rows['body']['rows']); + + // changing description attribute to be null by default instead of empty string + $patchNull = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string/description', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => null, + 'required' => false, + ]); + // creating a dummy doc with null description + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Dummy', + 'releaseYear' => 1944, + 'birthDay' => '1975-06-12 14:12:55+02:00', + 'actors' => [ + 'Dummy', + ], + ] + ]); + + $this->assertEquals(201, $row1['headers']['status-code']); + // fetching docs with cursor after the dummy doc with order attr description which is null + $rowsPaginated = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('dummy')->toString(), + Query::cursorAfter(new Document(['$id' => $row1['body']['$id']]))->toString() + ], + ]); + // should throw 400 as the order attr description of the selected doc is null + $this->assertEquals(400, $rowsPaginated['headers']['status-code']); + + // deleting the dummy doc created + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $row1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + return ['rows' => $rows['body']['rows'], 'databaseId' => $databaseId]; + } + + + /** + * @depends testListDocuments + */ + public function testGetDocument(array $data): void + { + $databaseId = $data['databaseId']; + foreach ($data['rows'] as $row) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows/' . $row['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($response['body']['$id'], $row['$id']); + $this->assertEquals($row['$tableId'], $response['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $response['body']); + $this->assertEquals($row['$databaseId'], $response['body']['$databaseId']); + $this->assertEquals($response['body']['title'], $row['title']); + $this->assertEquals($response['body']['releaseYear'], $row['releaseYear']); + $this->assertEquals($response['body']['$permissions'], $row['$permissions']); + $this->assertEquals($response['body']['birthDay'], $row['birthDay']); + $this->assertFalse(array_key_exists('$internalId', $response['body'])); + $this->assertFalse(array_key_exists('$tenant', $response['body'])); + } + } + + /** + * @depends testListDocuments + */ + public function testGetDocumentWithQueries(array $data): void + { + $databaseId = $data['databaseId']; + $row = $data['rows'][0]; + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows/' . $row['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['title', 'releaseYear', '$id'])->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($row['title'], $response['body']['title']); + $this->assertEquals($row['releaseYear'], $response['body']['releaseYear']); + $this->assertArrayNotHasKey('birthDay', $response['body']); + } + + /** + * @depends testCreateDocument + */ + public function testListDocumentsAfterPagination(array $data): array + { + $databaseId = $data['databaseId']; + /** + * Test after without order. + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $base['headers']['status-code']); + $this->assertEquals('Captain America', $base['body']['rows'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']); + $this->assertCount(3, $base['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString() + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($base['body']['rows'][1]['$id'], $rows['body']['rows'][0]['$id']); + $this->assertEquals($base['body']['rows'][2]['$id'], $rows['body']['rows'][1]['$id']); + $this->assertCount(2, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString() + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEmpty($rows['body']['rows']); + + /** + * Test with ASC order and after. + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('releaseYear')->toString() + ], + ]); + + $this->assertEquals(200, $base['headers']['status-code']); + $this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::orderAsc('releaseYear')->toString() + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($base['body']['rows'][2]['$id'], $rows['body']['rows'][0]['$id']); + $this->assertCount(1, $rows['body']['rows']); + + /** + * Test with DESC order and after. + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderDesc('releaseYear')->toString() + ], + ]); + + $this->assertEquals(200, $base['headers']['status-code']); + $this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::orderDesc('releaseYear')->toString() + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($base['body']['rows'][2]['$id'], $rows['body']['rows'][0]['$id']); + $this->assertCount(1, $rows['body']['rows']); + + /** + * Test after with unknown document. + */ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(), + ], + ]); + + $this->assertEquals(400, $rows['headers']['status-code']); + + /** + * Test null value for cursor + */ + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + '{"method":"cursorAfter","values":[null]}', + ], + ]); + + $this->assertEquals(400, $rows['headers']['status-code']); + + return []; + } + + /** + * @depends testCreateDocument + */ + public function testListDocumentsBeforePagination(array $data): array + { + $databaseId = $data['databaseId']; + /** + * Test before without order. + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $base['headers']['status-code']); + $this->assertEquals('Captain America', $base['body']['rows'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']); + $this->assertCount(3, $base['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($base['body']['rows'][0]['$id'], $rows['body']['rows'][0]['$id']); + $this->assertEquals($base['body']['rows'][1]['$id'], $rows['body']['rows'][1]['$id']); + $this->assertCount(2, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEmpty($rows['body']['rows']); + + /** + * Test with ASC order and after. + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('releaseYear')->toString(), + ], + ]); + + $this->assertEquals(200, $base['headers']['status-code']); + $this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::orderAsc('releaseYear')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($base['body']['rows'][0]['$id'], $rows['body']['rows'][0]['$id']); + $this->assertCount(1, $rows['body']['rows']); + + /** + * Test with DESC order and after. + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderDesc('releaseYear')->toString(), + ], + ]); + + $this->assertEquals(200, $base['headers']['status-code']); + $this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']); + $this->assertCount(3, $base['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(), + Query::orderDesc('releaseYear')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($base['body']['rows'][0]['$id'], $rows['body']['rows'][0]['$id']); + $this->assertCount(1, $rows['body']['rows']); + + return []; + } + + /** + * @depends testCreateDocument + */ + public function testListDocumentsLimitAndOffset(array $data): array + { + $databaseId = $data['databaseId']; + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('releaseYear')->toString(), + Query::limit(1)->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('releaseYear')->toString(), + Query::limit(2)->toString(), + Query::offset(1)->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(2017, $rows['body']['rows'][0]['releaseYear']); + $this->assertEquals(2019, $rows['body']['rows'][1]['releaseYear']); + $this->assertCount(2, $rows['body']['rows']); + + return []; + } + + /** + * @depends testCreateDocument + */ + public function testDocumentsListQueries(array $data): array + { + $databaseId = $data['databaseId']; + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::search('title', 'Captain America')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('$id', [$rows['body']['rows'][0]['$id']])->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::search('title', 'Homecoming')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(2017, $rows['body']['rows'][0]['releaseYear']); + $this->assertCount(1, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::search('title', 'spider')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(2019, $rows['body']['rows'][0]['releaseYear']); + $this->assertEquals(2017, $rows['body']['rows'][1]['releaseYear']); + $this->assertCount(2, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + '{"method":"contains","attribute":"title","values":[bad]}' + ], + ]); + + $this->assertEquals(400, $rows['headers']['status-code']); + $this->assertEquals('Invalid query: Syntax error', $rows['body']['message']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::contains('title', ['spi'])->toString(), // like query + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(2, $rows['body']['total']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('releaseYear', [1944])->toString(), + ], + ]); + + $this->assertCount(1, $rows['body']['rows']); + $this->assertEquals('Captain America', $rows['body']['rows'][0]['title']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::notEqual('releaseYear', 1944)->toString(), + ], + ]); + + $this->assertCount(2, $rows['body']['rows']); + $this->assertEquals('Spider-Man: Far From Home', $rows['body']['rows'][0]['title']); + $this->assertEquals('Spider-Man: Homecoming', $rows['body']['rows'][1]['title']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::greaterThan('$createdAt', '1976-06-12')->toString(), + ], + ]); + + $this->assertCount(3, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::lessThan('$createdAt', '1976-06-12')->toString(), + ], + ]); + + $this->assertCount(0, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::contains('actors', ['Tom Holland', 'Samuel Jackson'])->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(3, $rows['body']['total']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::contains('actors', ['Tom'])->toString(), // Full-match not like + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(0, $rows['body']['total']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::greaterThan('birthDay', '16/01/2024 12:00:00AM')->toString(), + ], + ]); + + $this->assertEquals(400, $rows['headers']['status-code']); + $this->assertEquals('Invalid query: Query value is invalid for attribute "birthDay"', $rows['body']['message']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::greaterThan('birthDay', '1960-01-01 10:10:10+02:30')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals('1975-06-12T12:12:55.000+00:00', $rows['body']['rows'][0]['birthDay']); + $this->assertEquals('1975-06-12T18:12:55.000+00:00', $rows['body']['rows'][1]['birthDay']); + $this->assertCount(2, $rows['body']['rows']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::isNull('integers')->toString(), + ], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1, $rows['body']['total']); + + /** + * Test for Failure + */ + $conditions = []; + + for ($i = 0; $i < APP_DATABASE_QUERY_MAX_VALUES + 1; $i++) { + $conditions[] = $i; + } + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('releaseYear', $conditions)->toString(), + ], + ]); + $this->assertEquals(400, $rows['headers']['status-code']); + $this->assertEquals('Invalid query: Query on attribute has greater than '.APP_DATABASE_QUERY_MAX_VALUES.' values: releaseYear', $rows['body']['message']); + + $value = ''; + + for ($i = 0; $i < 101; $i++) { + $value .= "[" . $i . "] Too long title to cross 2k chars query limit "; + } + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::search('title', $value)->toString(), + ], + ]); + + // Todo: Not sure what to do we with Query length Test VS old? JSON validator will fails if query string will be truncated? + //$this->assertEquals(400, $rows['headers']['status-code']); + + // Todo: Disabled for CL - Uncomment after ProxyDatabase cleanup for find method + // $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $this->getProject()['$id'], + // ], $this->getHeaders()), [ + // 'queries' => [ + // Query::search('actors', 'Tom')->toString(), + // ], + // ]); + // $this->assertEquals(400, $rows['headers']['status-code']); + // $this->assertEquals('Invalid query: Cannot query search on attribute "actors" because it is an array.', $rows['body']['message']); + + return []; + } + + /** + * @depends testCreateDocument + */ + public function testUpdateDocument(array $data): array + { + $databaseId = $data['databaseId']; + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Thor: Ragnaroc', + 'releaseYear' => 2017, + 'birthDay' => '1976-06-12 14:12:55', + 'actors' => [], + '$createdAt' => 5 // Should be ignored + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], + ]); + + $id = $row['body']['$id']; + + $this->assertEquals(201, $row['headers']['status-code']); + $this->assertEquals($data['moviesId'], $row['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $row['body']); + $this->assertEquals($databaseId, $row['body']['$databaseId']); + $this->assertEquals($row['body']['title'], 'Thor: Ragnaroc'); + $this->assertEquals($row['body']['releaseYear'], 2017); + $dateValidator = new DatetimeValidator(); + $this->assertEquals(true, $dateValidator->isValid($row['body']['$createdAt'])); + $this->assertEquals(true, $dateValidator->isValid($row['body']['birthDay'])); + $this->assertContains(Permission::read(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals($row['body']['$id'], $id); + $this->assertEquals($data['moviesId'], $row['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $row['body']); + $this->assertEquals($databaseId, $row['body']['$databaseId']); + $this->assertEquals($row['body']['title'], 'Thor: Ragnarok'); + $this->assertEquals($row['body']['releaseYear'], 2017); + $this->assertContains(Permission::read(Role::users()), $row['body']['$permissions']); + $this->assertContains(Permission::update(Role::users()), $row['body']['$permissions']); + $this->assertContains(Permission::delete(Role::users()), $row['body']['$permissions']); + + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $id = $row['body']['$id']; + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals($data['moviesId'], $row['body']['$tableId']); + $this->assertArrayNotHasKey('$table', $row['body']); + $this->assertEquals($databaseId, $row['body']['$databaseId']); + $this->assertEquals($row['body']['title'], 'Thor: Ragnarok'); + $this->assertEquals($row['body']['releaseYear'], 2017); + + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-timestamp' => DateTime::formatTz(DateTime::now()), + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + /** + * Test for failure + */ + + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-timestamp' => 'invalid', + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('Invalid X-Appwrite-Timestamp header value', $response['body']['message']); + $this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']); + + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-timestamp' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -1000)), + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + ]); + + $this->assertEquals(409, $response['headers']['status-code']); + $this->assertEquals('Remote row is newer than local.', $response['body']['message']); + $this->assertEquals(Exception::ROW_UPDATE_CONFLICT, $response['body']['type']); + + return []; + } + + /** + * @depends testCreateDocument + */ + public function testDeleteDocument(array $data): array + { + $databaseId = $data['databaseId']; + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Thor: Ragnarok', + 'releaseYear' => 2017, + 'birthDay' => '1975-06-12 14:12:55', + 'actors' => [], + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $id = $row['body']['$id']; + + $this->assertEquals(201, $row['headers']['status-code']); + + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + + $row = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(204, $row['headers']['status-code']); + + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $row['headers']['status-code']); + + return $data; + } + + public function testInvalidDocumentStructure() + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'InvalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('InvalidDocumentDatabase', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'invalidDocumentStructure', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals('invalidDocumentStructure', $table['body']['name']); + + $tableId = $table['body']['$id']; + + $email = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'email', + 'required' => false, + ]); + + $enum = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'enum', + 'elements' => ['yes', 'no', 'maybe'], + 'required' => false, + ]); + + $ip = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'ip', + 'required' => false, + ]); + + $url = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'url', + 'size' => 256, + 'required' => false, + ]); + + $range = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'range', + 'required' => false, + 'min' => 1, + 'max' => 10, + ]); + + // TODO@kodumbeats min and max are rounded in error message + $floatRange = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'floatRange', + 'required' => false, + 'min' => 1.1, + 'max' => 1.4, + ]); + + $probability = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'probability', + 'required' => false, + 'default' => 0, + 'min' => 0, + 'max' => 1, + ]); + + $upperBound = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'upperBound', + 'required' => false, + 'max' => 10, + ]); + + $lowerBound = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'lowerBound', + 'required' => false, + 'min' => 5, + ]); + + /** + * Test for failure + */ + + $invalidRange = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'invalidRange', + 'required' => false, + 'min' => 4, + 'max' => 3, + ]); + + $defaultArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'defaultArray', + 'required' => false, + 'default' => 42, + 'array' => true, + ]); + + $defaultRequired = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => ID::custom('defaultRequired'), + 'required' => true, + 'default' => 12 + ]); + + $enumDefault = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => ID::custom('enumDefault'), + 'elements' => ['north', 'west'], + 'default' => 'south' + ]); + + $enumDefaultStrict = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => ID::custom('enumDefault'), + 'elements' => ['north', 'west'], + 'default' => 'NORTH' + ]); + + $goodDatetime = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'birthDay', + 'required' => false, + 'default' => null + ]); + + $datetimeDefault = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'badBirthDay', + 'required' => false, + 'default' => 'bad' + ]); + + $this->assertEquals(202, $email['headers']['status-code']); + $this->assertEquals(202, $ip['headers']['status-code']); + $this->assertEquals(202, $url['headers']['status-code']); + $this->assertEquals(202, $range['headers']['status-code']); + $this->assertEquals(202, $floatRange['headers']['status-code']); + $this->assertEquals(202, $probability['headers']['status-code']); + $this->assertEquals(202, $upperBound['headers']['status-code']); + $this->assertEquals(202, $lowerBound['headers']['status-code']); + $this->assertEquals(202, $enum['headers']['status-code']); + $this->assertEquals(202, $goodDatetime['headers']['status-code']); + $this->assertEquals(400, $invalidRange['headers']['status-code']); + $this->assertEquals(400, $defaultArray['headers']['status-code']); + $this->assertEquals(400, $defaultRequired['headers']['status-code']); + $this->assertEquals(400, $enumDefault['headers']['status-code']); + $this->assertEquals(400, $enumDefaultStrict['headers']['status-code']); + $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']); + $this->assertEquals('Cannot set default value for array columns', $defaultArray['body']['message']); + $this->assertEquals(400, $datetimeDefault['headers']['status-code']); + // wait for worker to add attributes + sleep(3); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), []); + + $this->assertCount(10, $table['body']['columns']); + + /** + * Test for successful validation + */ + + $goodEmail = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'email' => 'user@example.com', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $goodEnum = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'enum' => 'yes', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $goodIp = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'ip' => '1.1.1.1', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $goodUrl = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'url' => 'http://www.example.com', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $goodRange = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'range' => 3, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $goodFloatRange = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'floatRange' => 1.4, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $goodProbability = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'probability' => 0.99999, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $notTooHigh = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'upperBound' => 8, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $notTooLow = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'lowerBound' => 8, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $this->assertEquals(201, $goodEmail['headers']['status-code']); + $this->assertEquals(201, $goodEnum['headers']['status-code']); + $this->assertEquals(201, $goodIp['headers']['status-code']); + $this->assertEquals(201, $goodUrl['headers']['status-code']); + $this->assertEquals(201, $goodRange['headers']['status-code']); + $this->assertEquals(201, $goodFloatRange['headers']['status-code']); + $this->assertEquals(201, $goodProbability['headers']['status-code']); + $this->assertEquals(201, $notTooHigh['headers']['status-code']); + $this->assertEquals(201, $notTooLow['headers']['status-code']); + + /* + * Test that custom validators reject documents + */ + + $badEmail = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'email' => 'user@@example.com', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badEnum = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'enum' => 'badEnum', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badIp = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'ip' => '1.1.1.1.1', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badUrl = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'url' => 'example...com', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badRange = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'range' => 11, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badFloatRange = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'floatRange' => 2.5, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badProbability = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'probability' => 1.1, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $tooHigh = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'upperBound' => 11, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $tooLow = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'lowerBound' => 3, + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $badTime = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'unique()', + 'data' => [ + 'birthDay' => '2020-10-10 27:30:10+01:00', + ], + 'read' => ['user:' . $this->getUser()['$id']], + 'write' => ['user:' . $this->getUser()['$id']], + ]); + + $this->assertEquals(400, $badEmail['headers']['status-code']); + $this->assertEquals(400, $badEnum['headers']['status-code']); + $this->assertEquals(400, $badIp['headers']['status-code']); + $this->assertEquals(400, $badUrl['headers']['status-code']); + $this->assertEquals(400, $badRange['headers']['status-code']); + $this->assertEquals(400, $badFloatRange['headers']['status-code']); + $this->assertEquals(400, $badProbability['headers']['status-code']); + $this->assertEquals(400, $tooHigh['headers']['status-code']); + $this->assertEquals(400, $tooLow['headers']['status-code']); + $this->assertEquals(400, $badTime['headers']['status-code']); + $this->assertEquals('Invalid document structure: Attribute "email" has invalid format. Value must be a valid email address', $badEmail['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "enum" has invalid format. Value must be one of (yes, no, maybe)', $badEnum['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "ip" has invalid format. Value must be a valid IP address', $badIp['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "url" has invalid format. Value must be a valid URL', $badUrl['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "range" has invalid format. Value must be a valid range between 1 and 10', $badRange['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,807', $tooLow['body']['message']); + } + + /** + * @depends testDeleteDocument + */ + public function testDefaultPermissions(array $data): array + { + $databaseId = $data['databaseId']; + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + 'releaseYear' => 1944, + 'actors' => [], + ], + ]); + + $id = $row['body']['$id']; + + $this->assertEquals(201, $row['headers']['status-code']); + $this->assertEquals($row['body']['title'], 'Captain America'); + $this->assertEquals($row['body']['releaseYear'], 1944); + $this->assertIsArray($row['body']['$permissions']); + + if ($this->getSide() == 'client') { + $this->assertCount(3, $row['body']['$permissions']); + $this->assertContains(Permission::read(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + } + + if ($this->getSide() == 'server') { + $this->assertCount(0, $row['body']['$permissions']); + $this->assertEquals([], $row['body']['$permissions']); + } + + // Updated Permissions + + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Captain America 2', + 'releaseYear' => 1945, + 'actors' => [], + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])) + ], + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals($row['body']['title'], 'Captain America 2'); + $this->assertEquals($row['body']['releaseYear'], 1945); + + // This differs from the old permissions model because we don't inherit + // existing document permissions on update, unless none were supplied, + // so that specific types can be removed if wanted. + $this->assertCount(2, $row['body']['$permissions']); + $this->assertEquals([ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + ], $row['body']['$permissions']); + + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals($row['body']['title'], 'Captain America 2'); + $this->assertEquals($row['body']['releaseYear'], 1945); + + $this->assertCount(2, $row['body']['$permissions']); + $this->assertEquals([ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + ], $row['body']['$permissions']); + + // Reset Permissions + + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Captain America 3', + 'releaseYear' => 1946, + 'actors' => [], + ], + 'permissions' => [], + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals($row['body']['title'], 'Captain America 3'); + $this->assertEquals($row['body']['releaseYear'], 1946); + $this->assertCount(0, $row['body']['$permissions']); + $this->assertEquals([], $row['body']['$permissions']); + + // Check client side can no longer read the document. + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + switch ($this->getSide()) { + case 'client': + $this->assertEquals(404, $row['headers']['status-code']); + break; + case 'server': + $this->assertEquals(200, $row['headers']['status-code']); + break; + } + + return $data; + } + + public function testEnforceCollectionAndDocumentPermissions(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'EnforceCollectionAndDocumentPermissions', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('EnforceCollectionAndDocumentPermissions', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $user = $this->getUser()['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'enforceCollectionAndDocumentPermissions', + 'documentSecurity' => true, + 'permissions' => [ + Permission::read(Role::user($user)), + Permission::create(Role::user($user)), + Permission::update(Role::user($user)), + Permission::delete(Role::user($user)), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'enforceCollectionAndDocumentPermissions'); + $this->assertEquals($table['body']['documentSecurity'], true); + + $tableId = $table['body']['$id']; + + sleep(2); + + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'attribute', + 'size' => 64, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code'], 202); + $this->assertEquals('attribute', $attribute['body']['key']); + + // wait for db to add attribute + sleep(2); + + $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'key_attribute', + 'type' => 'key', + 'columns' => [$attribute['body']['key']], + ]); + + $this->assertEquals(202, $index['headers']['status-code']); + $this->assertEquals('key_attribute', $index['body']['key']); + + // wait for db to add attribute + sleep(2); + + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'one', + ], + 'permissions' => [ + Permission::read(Role::user($user)), + Permission::update(Role::user($user)), + Permission::delete(Role::user($user)), + ] + ]); + + $this->assertEquals(201, $row1['headers']['status-code']); + + $row2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'one', + ], + 'permissions' => [ + Permission::update(Role::user($user)), + Permission::delete(Role::user($user)), + ] + ]); + + $this->assertEquals(201, $row2['headers']['status-code']); + + $row3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'one', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom('other'))), + Permission::update(Role::user(ID::custom('other'))), + ], + ]); + + $this->assertEquals(201, $row3['headers']['status-code']); + + $rowsUser1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Current user has read permission on the collection so can get any document + $this->assertEquals(3, $rowsUser1['body']['total']); + $this->assertCount(3, $rowsUser1['body']['rows']); + + $row3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Current user has read permission on the collection so can get any document + $this->assertEquals(200, $row3GetWithCollectionRead['headers']['status-code']); + + $email = uniqid() . 'user@localhost.test'; + $password = 'password'; + $name = 'User Name'; + $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'userId' => ID::custom('other'), + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + $session2 = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'email' => $email, + 'password' => $password, + ]); + $session2 = $session2['cookies']['a_session_' . $this->getProject()['$id']]; + + $row3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, + ]); + + // Current user has no collection permissions but has read permission for this document + $this->assertEquals(200, $row3GetWithDocumentRead['headers']['status-code']); + + $row2GetFailure = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row2['body']['$id'], [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, + ]); + + // Current user has no collection or document permissions for this document + $this->assertEquals(404, $row2GetFailure['headers']['status-code']); + + $rowsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, + ]); + + // Current user has no collection permissions but has read permission for one document + $this->assertEquals(1, $rowsUser2['body']['total']); + $this->assertCount(1, $rowsUser2['body']['rows']); + } + + public function testEnforceCollectionPermissions() + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'EnforceCollectionPermissions', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('EnforceCollectionPermissions', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $user = $this->getUser()['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'enforceCollectionPermissions', + 'permissions' => [ + Permission::read(Role::user($user)), + Permission::create(Role::user($user)), + Permission::update(Role::user($user)), + Permission::delete(Role::user($user)), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'enforceCollectionPermissions'); + $this->assertEquals($table['body']['documentSecurity'], false); + + $tableId = $table['body']['$id']; + + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'attribute', + 'size' => 64, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code'], 202); + $this->assertEquals('attribute', $attribute['body']['key']); + + \sleep(2); + + $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'key_attribute', + 'type' => 'key', + 'columns' => [$attribute['body']['key']], + ]); + + $this->assertEquals(202, $index['headers']['status-code']); + $this->assertEquals('key_attribute', $index['body']['key']); + + \sleep(2); + + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'one', + ], + 'permissions' => [ + Permission::read(Role::user($user)), + Permission::update(Role::user($user)), + Permission::delete(Role::user($user)), + ] + ]); + + $this->assertEquals(201, $row1['headers']['status-code']); + + $row2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'one', + ], + 'permissions' => [ + Permission::update(Role::user($user)), + Permission::delete(Role::user($user)), + ] + ]); + + $this->assertEquals(201, $row2['headers']['status-code']); + + $row3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'one', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom('other2'))), + Permission::update(Role::user(ID::custom('other2'))), + ], + ]); + + $this->assertEquals(201, $row3['headers']['status-code']); + + $rowsUser1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Current user has read permission on the collection so can get any document + $this->assertEquals(3, $rowsUser1['body']['total']); + $this->assertCount(3, $rowsUser1['body']['rows']); + + $row3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Current user has read permission on the collection so can get any document + $this->assertEquals(200, $row3GetWithCollectionRead['headers']['status-code']); + + $email = uniqid() . 'user2@localhost.test'; + $password = 'password'; + $name = 'User Name'; + $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'userId' => ID::custom('other2'), + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + $session2 = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'email' => $email, + 'password' => $password, + ]); + $session2 = $session2['cookies']['a_session_' . $this->getProject()['$id']]; + + $row3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, + ]); + + // other2 has no collection permissions and document permissions are disabled + $this->assertEquals(404, $row3GetWithDocumentRead['headers']['status-code']); + + $rowsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, + ]); + + // other2 has no collection permissions and document permissions are disabled + $this->assertEquals(401, $rowsUser2['headers']['status-code']); + + // Enable document permissions + $table = $this->client->call(CLient::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $tableId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'name' => $table['body']['name'], + 'documentSecurity' => true, + ]); + + $rowsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, + ]); + + // Current user has no collection permissions read access to one document + $this->assertEquals(1, $rowsUser2['body']['total']); + $this->assertCount(1, $rowsUser2['body']['rows']); + } + + /** + * @depends testDefaultPermissions + */ + public function testUniqueIndexDuplicate(array $data): array + { + $databaseId = $data['databaseId']; + $uniqueIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'unique_title', + 'type' => 'unique', + 'columns' => ['title'], + ]); + + $this->assertEquals(202, $uniqueIndex['headers']['status-code']); + + sleep(2); + + // test for failure + $duplicate = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + 'releaseYear' => 1944, + 'actors' => [ + 'Chris Evans', + 'Samuel Jackson', + ] + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $this->assertEquals(409, $duplicate['headers']['status-code']); + + // Test for exception when updating document to conflict + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America 5', + 'releaseYear' => 1944, + 'actors' => [ + 'Chris Evans', + 'Samuel Jackson', + ] + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $this->assertEquals(201, $row['headers']['status-code']); + + // Test for exception when updating document to conflict + $duplicate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + 'releaseYear' => 1944, + 'actors' => [ + 'Chris Evans', + 'Samuel Jackson', + ] + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $this->assertEquals(409, $duplicate['headers']['status-code']); + + return $data; + } + + /** + * @depends testUniqueIndexDuplicate + */ + public function testPersistantCreatedAt(array $data): array + { + $headers = $this->getSide() === 'client' ? array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()) : [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables/' . $data['moviesId'] . '/rows', $headers, [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Creation Date Test', + 'releaseYear' => 2000 + ] + ]); + + $this->assertEquals($row['body']['title'], 'Creation Date Test'); + + $rowId = $row['body']['$id']; + $createdAt = $row['body']['$createdAt']; + $updatedAt = $row['body']['$updatedAt']; + + \sleep(1); + + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, $headers, [ + 'data' => [ + 'title' => 'Updated Date Test', + ] + ]); + + $updatedAtSecond = $row['body']['$updatedAt']; + + $this->assertEquals($row['body']['title'], 'Updated Date Test'); + $this->assertEquals($row['body']['$createdAt'], $createdAt); + $this->assertNotEquals($row['body']['$updatedAt'], $updatedAt); + + \sleep(1); + + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, $headers, [ + 'data' => [ + 'title' => 'Again Updated Date Test', + '$createdAt' => '2022-08-01 13:09:23.040', // $createdAt is not updatable + '$updatedAt' => '2022-08-01 13:09:23.050' // system will update it not api + ] + ]); + + $this->assertEquals($row['body']['title'], 'Again Updated Date Test'); + $this->assertEquals($row['body']['$createdAt'], $createdAt); + $this->assertNotEquals($row['body']['$createdAt'], '2022-08-01 13:09:23.040'); + $this->assertNotEquals($row['body']['$updatedAt'], $updatedAt); + $this->assertNotEquals($row['body']['$updatedAt'], $updatedAtSecond); + $this->assertNotEquals($row['body']['$updatedAt'], '2022-08-01 13:09:23.050'); + + return $data; + } + + public function testUpdatePermissionsWithEmptyPayload(): array + { + // Create Database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Empty Permissions', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + + $databaseId = $database['body']['$id']; + + // Create collection + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::create(Role::user(ID::custom($this->getUser()['$id']))), + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $movies['headers']['status-code']); + $this->assertEquals($movies['body']['name'], 'Movies'); + + $moviesId = $movies['body']['$id']; + + // create attribute + $title = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $title['headers']['status-code']); + + // wait for database worker to create attributes + sleep(2); + + // add document + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $id = $row['body']['$id']; + + $this->assertEquals(201, $row['headers']['status-code']); + $this->assertCount(3, $row['body']['$permissions']); + $this->assertContains(Permission::read(Role::any()), $row['body']['$permissions']); + $this->assertContains(Permission::update(Role::any()), $row['body']['$permissions']); + $this->assertContains(Permission::delete(Role::any()), $row['body']['$permissions']); + + // Send only read permission + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertCount(1, $row['body']['$permissions']); + + // Send only mutation permissions + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'permissions' => [ + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ], + ]); + + if ($this->getSide() == 'server') { + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertCount(2, $row['body']['$permissions']); + $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); + } + + // remove collection + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $moviesId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return []; + } + + /** + * @depends testCreateDatabase + */ + public function testAttributeBooleanDefault(array $data): void + { + $databaseId = $data['databaseId']; + + /** + * Test for SUCCESS + */ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Boolean' + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + + $tableId = $table['body']['$id']; + + $true = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'true', + 'required' => false, + 'default' => true + ]); + + $this->assertEquals(202, $true['headers']['status-code']); + + $false = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'false', + 'required' => false, + 'default' => false + ]); + + $this->assertEquals(202, $false['headers']['status-code']); + } + + /** + * @depends testCreateDatabase + */ + public function testOneToOneRelationship(array $data): array + { + $databaseId = $data['databaseId']; + + $person = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'person', + 'name' => 'person', + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + Permission::create(Role::user($this->getUser()['$id'])), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $person['headers']['status-code']); + + $library = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'library', + 'name' => 'library', + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::create(Role::user($this->getUser()['$id'])), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $library['headers']['status-code']); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'fullName', + 'size' => 255, + 'required' => false, + ]); + + sleep(1); // Wait for worker + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => 'library', + 'type' => Database::RELATION_ONE_TO_ONE, + 'key' => 'library', + 'twoWay' => true, + 'onDelete' => Database::RELATION_MUTATE_CASCADE, + ]); + + sleep(1); // Wait for worker + + $libraryName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $library['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'libraryName', + 'size' => 255, + 'required' => true, + ]); + + sleep(1); // Wait for worker + + $this->assertEquals(202, $libraryName['headers']['status-code']); + $this->assertEquals(202, $relation['headers']['status-code']); + $this->assertEquals('library', $relation['body']['key']); + $this->assertEquals('relationship', $relation['body']['type']); + $this->assertEquals('processing', $relation['body']['status']); + + $columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $columns['headers']['status-code']); + $this->assertEquals(2, $columns['body']['total']); + $columns = $columns['body']['columns']; + $this->assertEquals('library', $columns[1]['relatedTable']); + $this->assertEquals('oneToOne', $columns[1]['relationType']); + $this->assertEquals(true, $columns[1]['twoWay']); + $this->assertEquals('person', $columns[1]['twoWayKey']); + $this->assertEquals(Database::RELATION_MUTATE_CASCADE, $columns[1]['onDelete']); + + $attribute = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$person['body']['$id']}/columns/library", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals('available', $attribute['body']['status']); + $this->assertEquals('library', $attribute['body']['key']); + $this->assertEquals('relationship', $attribute['body']['type']); + $this->assertEquals(false, $attribute['body']['required']); + $this->assertEquals(false, $attribute['body']['array']); + $this->assertEquals('oneToOne', $attribute['body']['relationType']); + $this->assertEquals(true, $attribute['body']['twoWay']); + $this->assertEquals('person', $attribute['body']['twoWayKey']); + $this->assertEquals(Database::RELATION_MUTATE_CASCADE, $attribute['body']['onDelete']); + + $person1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'library' => [ + '$id' => 'library1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'libraryName' => 'Library 1', + ], + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertEquals('Library 1', $person1['body']['library']['libraryName']); + + // Create without nested ID + $person2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'library' => [ + 'libraryName' => 'Library 2', + ], + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertEquals('Library 2', $person2['body']['library']['libraryName']); + + // Ensure IDs were set and internal IDs removed + $this->assertEquals($databaseId, $person1['body']['$databaseId']); + $this->assertEquals($databaseId, $person1['body']['library']['$databaseId']); + + $this->assertEquals($person['body']['$id'], $person1['body']['$tableId']); + $this->assertEquals($library['body']['$id'], $person1['body']['library']['$tableId']); + + $this->assertArrayNotHasKey('$table', $person1['body']); + $this->assertArrayNotHasKey('$table', $person1['body']['library']); + $this->assertArrayNotHasKey('$internalId', $person1['body']); + $this->assertArrayNotHasKey('$internalId', $person1['body']['library']); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['fullName', 'library.*'])->toString(), + Query::equal('library', ['library1'])->toString(), + ], + ]); + + $this->assertEquals(1, $rows['body']['total']); + $this->assertEquals('Library 1', $rows['body']['rows'][0]['library']['libraryName']); + $this->assertArrayHasKey('fullName', $rows['body']['rows'][0]); + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('library.libraryName', ['Library 1'])->toString(), + ], + ]); + + $this->assertEquals(400, $rows['headers']['status-code']); + $this->assertEquals('Invalid query: Cannot query nested attribute on: library', $rows['body']['message']); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/library', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + sleep(2); + + $this->assertEquals(204, $response['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$person['body']['$id']}/columns/library", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(404, $attribute['headers']['status-code']); + + $person1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/' . $person1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertArrayNotHasKey('library', $person1['body']); + + //Test Deletion of related twoKey + $columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $library['body']['$id'] . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $columns['headers']['status-code']); + $this->assertEquals(1, $columns['body']['total']); + $this->assertEquals('libraryName', $columns['body']['columns'][0]['key']); + + return [ + 'databaseId' => $databaseId, + 'personCollection' => $person['body']['$id'], + 'libraryCollection' => $library['body']['$id'], + ]; + } + + /** + * @depends testOneToOneRelationship + */ + public function testOneToManyRelationship(array $data): array + { + $databaseId = $data['databaseId']; + $personCollection = $data['personCollection']; + $libraryCollection = $data['libraryCollection']; + + // One person can own several libraries + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $personCollection . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => 'library', + 'type' => Database::RELATION_ONE_TO_MANY, + 'twoWay' => true, + 'key' => 'libraries', + 'twoWayKey' => 'person_one_to_many', + ]); + + sleep(1); + + $libraryAttributesResponse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $libraryCollection . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertIsArray($libraryAttributesResponse['body']['columns']); + $this->assertEquals(2, $libraryAttributesResponse['body']['total']); + $this->assertEquals('person_one_to_many', $libraryAttributesResponse['body']['columns'][1]['key']); + + $libraryCollectionResponse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $libraryCollection, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertIsArray($libraryCollectionResponse['body']['columns']); + $this->assertCount(2, $libraryCollectionResponse['body']['columns']); + + $attribute = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$personCollection}/columns/libraries", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals('available', $attribute['body']['status']); + $this->assertEquals('libraries', $attribute['body']['key']); + $this->assertEquals('relationship', $attribute['body']['type']); + $this->assertEquals(false, $attribute['body']['required']); + $this->assertEquals(false, $attribute['body']['array']); + $this->assertEquals('oneToMany', $attribute['body']['relationType']); + $this->assertEquals(true, $attribute['body']['twoWay']); + $this->assertEquals('person_one_to_many', $attribute['body']['twoWayKey']); + $this->assertEquals('restrict', $attribute['body']['onDelete']); + + $person2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $personCollection . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'person10', + 'data' => [ + 'fullName' => 'Stevie Wonder', + 'libraries' => [ + [ + '$id' => 'library10', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'libraryName' => 'Library 10', + ], + [ + '$id' => 'library11', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'libraryName' => 'Library 11', + ] + ], + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ] + ]); + + $this->assertEquals(201, $person2['headers']['status-code']); + $this->assertArrayHasKey('libraries', $person2['body']); + $this->assertEquals(2, count($person2['body']['libraries'])); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $personCollection . '/rows/' . $person2['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayNotHasKey('$table', $response['body']); + $this->assertArrayHasKey('libraries', $response['body']); + $this->assertEquals(2, count($response['body']['libraries'])); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $libraryCollection . '/rows/library11', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayHasKey('person_one_to_many', $response['body']); + $this->assertEquals('person10', $response['body']['person_one_to_many']['$id']); + + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $personCollection . '/columns/libraries/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'onDelete' => Database::RELATION_MUTATE_CASCADE, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$personCollection}/columns/libraries", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals('available', $attribute['body']['status']); + $this->assertEquals('libraries', $attribute['body']['key']); + $this->assertEquals('relationship', $attribute['body']['type']); + $this->assertEquals(false, $attribute['body']['required']); + $this->assertEquals(false, $attribute['body']['array']); + $this->assertEquals('oneToMany', $attribute['body']['relationType']); + $this->assertEquals(true, $attribute['body']['twoWay']); + $this->assertEquals(Database::RELATION_MUTATE_CASCADE, $attribute['body']['onDelete']); + + return ['databaseId' => $databaseId, 'personCollection' => $personCollection]; + } + + /** + * @depends testCreateDatabase + */ + public function testManyToOneRelationship(array $data): array + { + $databaseId = $data['databaseId']; + + // Create album collection + $albums = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Albums', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + // Create album name attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ]); + + // Create artist collection + $artists = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Artists', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + // Create artist name attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $artists['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ]); + + // Create relationship + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $artists['body']['$id'], + 'type' => Database::RELATION_MANY_TO_ONE, + 'twoWay' => true, + 'key' => 'artist', + 'twoWayKey' => 'albums', + ]); + $this->assertEquals(202, $response['headers']['status-code']); + $this->assertEquals('artist', $response['body']['key']); + $this->assertEquals('relationship', $response['body']['type']); + $this->assertEquals(false, $response['body']['required']); + $this->assertEquals(false, $response['body']['array']); + $this->assertEquals('manyToOne', $response['body']['relationType']); + $this->assertEquals(true, $response['body']['twoWay']); + $this->assertEquals('albums', $response['body']['twoWayKey']); + $this->assertEquals('restrict', $response['body']['onDelete']); + + sleep(1); // Wait for worker + + $permissions = [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ]; + + // Create album + $album = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'album1', + 'permissions' => $permissions, + 'data' => [ + 'name' => 'Album 1', + 'artist' => [ + '$id' => ID::unique(), + 'name' => 'Artist 1', + ], + ], + ]); + + $this->assertEquals(201, $album['headers']['status-code']); + $this->assertEquals('album1', $album['body']['$id']); + $this->assertEquals('Album 1', $album['body']['name']); + $this->assertEquals('Artist 1', $album['body']['artist']['name']); + $this->assertEquals($permissions, $album['body']['$permissions']); + $this->assertEquals($permissions, $album['body']['artist']['$permissions']); + + $album = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/rows/album1', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $album['headers']['status-code']); + $this->assertEquals('album1', $album['body']['$id']); + $this->assertEquals('Album 1', $album['body']['name']); + $this->assertEquals('Artist 1', $album['body']['artist']['name']); + $this->assertEquals($permissions, $album['body']['$permissions']); + $this->assertEquals($permissions, $album['body']['artist']['$permissions']); + + $artist = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $artists['body']['$id'] . '/rows/' . $album['body']['artist']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $artist['headers']['status-code']); + $this->assertEquals('Artist 1', $artist['body']['name']); + $this->assertEquals($permissions, $artist['body']['$permissions']); + $this->assertEquals(1, count($artist['body']['albums'])); + $this->assertEquals('album1', $artist['body']['albums'][0]['$id']); + $this->assertEquals('Album 1', $artist['body']['albums'][0]['name']); + $this->assertEquals($permissions, $artist['body']['albums'][0]['$permissions']); + + return [ + 'databaseId' => $databaseId, + 'albumsCollection' => $albums['body']['$id'], + 'artistsCollection' => $artists['body']['$id'], + ]; + } + + /** + * @depends testCreateDatabase + */ + public function testManyToManyRelationship(array $data): array + { + $databaseId = $data['databaseId']; + + // Create sports collection + $sports = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Sports', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + // Create sport name attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ]); + + // Create player collection + $players = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Players', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + // Create player name attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $players['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ]); + + // Create relationship + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $players['body']['$id'], + 'type' => Database::RELATION_MANY_TO_MANY, + 'twoWay' => true, + 'key' => 'players', + 'twoWayKey' => 'sports', + 'onDelete' => Database::RELATION_MUTATE_SET_NULL, + ]); + + $this->assertEquals(202, $response['headers']['status-code']); + $this->assertEquals('players', $response['body']['key']); + $this->assertEquals('relationship', $response['body']['type']); + $this->assertEquals(false, $response['body']['required']); + $this->assertEquals(false, $response['body']['array']); + $this->assertEquals('manyToMany', $response['body']['relationType']); + $this->assertEquals(true, $response['body']['twoWay']); + $this->assertEquals('sports', $response['body']['twoWayKey']); + $this->assertEquals('setNull', $response['body']['onDelete']); + + sleep(1); // Wait for worker + + $permissions = [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + ]; + + // Create sport + $sport = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'sport1', + 'permissions' => $permissions, + 'data' => [ + 'name' => 'Sport 1', + 'players' => [ + [ + '$id' => 'player1', + 'name' => 'Player 1', + ], + [ + '$id' => 'player2', + 'name' => 'Player 2', + ] + ], + ], + ]); + + $this->assertEquals(201, $sport['headers']['status-code']); + $this->assertEquals('sport1', $sport['body']['$id']); + $this->assertEquals('Sport 1', $sport['body']['name']); + $this->assertEquals('Player 1', $sport['body']['players'][0]['name']); + $this->assertEquals('Player 2', $sport['body']['players'][1]['name']); + $this->assertEquals($permissions, $sport['body']['$permissions']); + $this->assertEquals($permissions, $sport['body']['players'][0]['$permissions']); + $this->assertEquals($permissions, $sport['body']['players'][1]['$permissions']); + + $sport = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/rows/sport1', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $sport['headers']['status-code']); + $this->assertEquals('sport1', $sport['body']['$id']); + $this->assertEquals('Sport 1', $sport['body']['name']); + $this->assertEquals('Player 1', $sport['body']['players'][0]['name']); + $this->assertEquals('Player 2', $sport['body']['players'][1]['name']); + $this->assertEquals($permissions, $sport['body']['$permissions']); + $this->assertEquals($permissions, $sport['body']['players'][0]['$permissions']); + $this->assertEquals($permissions, $sport['body']['players'][1]['$permissions']); + + $player = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $players['body']['$id'] . '/rows/' . $sport['body']['players'][0]['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $player['headers']['status-code']); + $this->assertEquals('Player 1', $player['body']['name']); + $this->assertEquals($permissions, $player['body']['$permissions']); + $this->assertEquals(1, count($player['body']['sports'])); + $this->assertEquals('sport1', $player['body']['sports'][0]['$id']); + $this->assertEquals('Sport 1', $player['body']['sports'][0]['name']); + $this->assertEquals($permissions, $player['body']['sports'][0]['$permissions']); + + return [ + 'databaseId' => $databaseId, + 'sportsCollection' => $sports['body']['$id'], + 'playersCollection' => $players['body']['$id'], + ]; + } + + /** + * @depends testOneToManyRelationship + */ + public function testValidateOperators(array $data): void + { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::isNotNull('$id')->toString(), + Query::startsWith('fullName', 'Stevie')->toString(), + Query::endsWith('fullName', 'Wonder')->toString(), + Query::between('$createdAt', '1975-12-06', '2050-12-01')->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, count($response['body']['rows'])); + $this->assertEquals('person10', $response['body']['rows'][0]['$id']); + $this->assertEquals('Stevie Wonder', $response['body']['rows'][0]['fullName']); + $this->assertEquals(2, count($response['body']['rows'][0]['libraries'])); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::isNotNull('$id')->toString(), + Query::isNull('fullName')->toString(), + Query::select(['fullName'])->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, count($response['body']['rows'])); + $this->assertEquals(null, $response['body']['rows'][0]['fullName']); + $this->assertArrayNotHasKey("libraries", $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); + } + + /** + * @depends testOneToManyRelationship + */ + public function testSelectQueries(array $data): void + { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('fullName', ['Stevie Wonder'])->toString(), + Query::select(['fullName'])->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayNotHasKey('libraries', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['libraries.*', '$id'])->toString(), + ], + ]); + $row = $response['body']['rows'][0]; + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayHasKey('libraries', $row); + $this->assertArrayNotHasKey('$databaseId', $row); + $this->assertArrayNotHasKey('$tableId', $row); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows/' . $row['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['fullName', '$id'])->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayHasKey('fullName', $response['body']); + $this->assertArrayNotHasKey('libraries', $response['body']); + } + + /** + * @throws \Utopia\Database\Exception + * @throws \Utopia\Database\Exception\Query + */ + public function testOrQueries(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Or queries' + ]); + + $this->assertNotEmpty($database['body']['$id']); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('Or queries', $database['body']['name']); + + $databaseId = $database['body']['$id']; + + // Create Collection + $presidents = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'USA Presidents', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $presidents['headers']['status-code']); + $this->assertEquals($presidents['body']['name'], 'USA Presidents'); + + // Create Attributes + $firstName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'first_name', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(202, $firstName['headers']['status-code']); + + $lastName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'last_name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $lastName['headers']['status-code']); + + // Wait for worker + sleep(2); + + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'first_name' => 'Donald', + 'last_name' => 'Trump', + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $row1['headers']['status-code']); + + $row2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'first_name' => 'George', + 'last_name' => 'Bush', + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $row2['headers']['status-code']); + + $row3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'first_name' => 'Joe', + 'last_name' => 'Biden', + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertEquals(201, $row3['headers']['status-code']); + + $rows = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'queries' => [ + Query::select(['first_name', 'last_name'])->toString(), + Query::or([ + Query::equal('first_name', ['Donald']), + Query::equal('last_name', ['Bush']) + ])->toString(), + Query::limit(999)->toString(), + Query::offset(0)->toString() + ], + ] + ); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertCount(2, $rows['body']['rows']); + } + + /** + * @depends testCreateDatabase + * @param array $data + * @return void + * @throws \Exception + */ + public function testUpdateWithExistingRelationships(array $data): void + { + $databaseId = $data['databaseId']; + + $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Collection1', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Collection2', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + $table1 = $table1['body']['$id']; + $table2 = $table2['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1 . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => '49', + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table2 . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => '49', + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1 . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2, + 'type' => Database::RELATION_ONE_TO_MANY, + 'twoWay' => true, + 'key' => 'collection2' + ]); + + sleep(1); + + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1 . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'name' => 'Document 1', + 'collection2' => [ + [ + 'name' => 'Document 2', + ], + ], + ], + ]); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1 . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'data' => [ + 'name' => 'Document 1 Updated', + ], + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + } + + /** + * @depends testCreateDatabase + */ + public function testTimeout(array $data): void + { + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Slow Queries', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + + $data = [ + '$id' => $table['body']['$id'], + 'databaseId' => $table['body']['databaseId'] + ]; + + $longtext = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'longtext', + 'size' => 100000000, + 'required' => false, + 'default' => null, + ]); + + $this->assertEquals($longtext['headers']['status-code'], 202); + + for ($i = 0; $i < 10; $i++) { + $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'longtext' => file_get_contents(__DIR__ . '../../../../../resources/longtext.txt'), + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + } + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-timeout' => 1, + ], $this->getHeaders()), [ + 'queries' => [ + Query::notEqual('longtext', 'appwrite')->toString(), + ], + ]); + + $this->assertEquals(408, $response['headers']['status-code']); + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php new file mode 100644 index 0000000000..48707d444b --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php @@ -0,0 +1,336 @@ +client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'databaseId' => ID::unique(), + 'name' => 'invalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertTrue($database['body']['enabled']); + + $databaseId = $database['body']['$id']; + + /** + * Test for SUCCESS + */ + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $movies['headers']['status-code']); + $this->assertEquals($movies['body']['name'], 'Movies'); + + /** + * Test when database is disabled but can still create collections + */ + $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'invalidDocumentDatabase Updated', + 'enabled' => false, + ]); + + $this->assertFalse($database['body']['enabled']); + + $tvShows = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'tableId' => ID::unique(), + 'name' => 'TvShows', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + /** + * Test when collection is disabled but can still modify collections + */ + $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $movies['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Movies', + 'enabled' => false, + ]); + + $this->assertEquals(201, $tvShows['headers']['status-code']); + $this->assertEquals($tvShows['body']['name'], 'TvShows'); + + return ['moviesId' => $movies['body']['$id'], 'databaseId' => $databaseId, 'tvShowsId' => $tvShows['body']['$id']]; + } + + /** + * @depends testCreateCollection + * @param array $data + * @throws \Exception + */ + public function testListCollection(array $data) + { + /** + * Test when database is disabled but can still call list collections + */ + $databaseId = $data['databaseId']; + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders())); + + $this->assertEquals(200, $tables['headers']['status-code']); + $this->assertEquals(2, $tables['body']['total']); + } + + /** + * @depends testCreateCollection + * @param array $data + * @throws \Exception + */ + public function testGetCollection(array $data) + { + $databaseId = $data['databaseId']; + $moviesCollectionId = $data['moviesId']; + + /** + * Test when database and collection are disabled but can still call get collection + */ + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $moviesCollectionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertEquals('Movies', $table['body']['name']); + $this->assertEquals($moviesCollectionId, $table['body']['$id']); + $this->assertFalse($table['body']['enabled']); + } + + /** + * @depends testCreateCollection + * @param array $data + * @throws \Exception + * @throws \Exception + */ + public function testUpdateCollection(array $data) + { + $databaseId = $data['databaseId']; + $moviesCollectionId = $data['moviesId']; + + /** + * Test When database and collection are disabled but can still call update collection + */ + $table = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $moviesCollectionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Movies Updated', + 'enabled' => false + ]); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertEquals('Movies Updated', $table['body']['name']); + $this->assertEquals($moviesCollectionId, $table['body']['$id']); + $this->assertFalse($table['body']['enabled']); + } + + /** + * @depends testCreateCollection + * @param array $data + * @throws \Exception + * @throws \Exception + */ + public function testDeleteCollection(array $data) + { + $databaseId = $data['databaseId']; + $tvShowsId = $data['tvShowsId']; + + /** + * Test when database and collection are disabled but can still call delete collection + */ + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tvShowsId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEquals($response['body'], ""); + } + + /** + * @depends testCreateCollection + */ + public function testGetDatabaseUsage(array $data) + { + $databaseId = $data['databaseId']; + /** + * Test for FAILURE + */ + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(15, count($response['body'])); + $this->assertEquals('24h', $response['body']['range']); + $this->assertIsNumeric($response['body']['rowsTotal']); + $this->assertIsNumeric($response['body']['tablesTotal']); + $this->assertIsArray($response['body']['tables']); + $this->assertIsArray($response['body']['rows']); + } + + + /** + * @depends testCreateCollection + */ + public function testGetCollectionUsage(array $data) + { + $databaseId = $data['databaseId']; + /** + * Test for FAILURE + */ + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/randomCollectionId/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(3, count($response['body'])); + $this->assertEquals('24h', $response['body']['range']); + $this->assertIsNumeric($response['body']['rowsTotal']); + $this->assertIsArray($response['body']['rows']); + } + + /** + * @depends testCreateCollection + * @throws \Utopia\Database\Exception\Query + */ + public function testGetCollectionLogs(array $data) + { + $databaseId = $data['databaseId']; + /** + * Test for SUCCESS + */ + $logs = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/logs', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $logs['headers']['status-code']); + $this->assertIsArray($logs['body']['logs']); + $this->assertIsNumeric($logs['body']['total']); + + $logs = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/logs', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::limit(1)->toString()] + ]); + + $this->assertEquals(200, $logs['headers']['status-code']); + $this->assertIsArray($logs['body']['logs']); + $this->assertLessThanOrEqual(1, count($logs['body']['logs'])); + $this->assertIsNumeric($logs['body']['total']); + + $logs = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/logs', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::offset(1)->toString()] + ]); + + $this->assertEquals(200, $logs['headers']['status-code']); + $this->assertIsArray($logs['body']['logs']); + $this->assertIsNumeric($logs['body']['total']); + + $logs = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/logs', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::offset(1)->toString(), Query::limit(1)->toString()] + ]); + + $this->assertEquals(200, $logs['headers']['status-code']); + $this->assertIsArray($logs['body']['logs']); + $this->assertLessThanOrEqual(1, count($logs['body']['logs'])); + $this->assertIsNumeric($logs['body']['total']); + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php new file mode 100644 index 0000000000..ca8ae08ca0 --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php @@ -0,0 +1,893 @@ +client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Test Database' + ]); + + $databaseId = $database['body']['$id']; + + // Collection aliases write to create, update, delete + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'documentSecurity' => true, + 'permissions' => [ + Permission::write(Role::user($this->getUser()['$id'])), + ], + ]); + + $moviesId = $movies['body']['$id']; + + $this->assertContains(Permission::create(Role::user($this->getUser()['$id'])), $movies['body']['$permissions']); + $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $movies['body']['$permissions']); + $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $movies['body']['$permissions']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + sleep(1); + + $this->assertEquals(202, $response['headers']['status-code']); + + // Document aliases write to update, delete + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + ], + 'permissions' => [ + Permission::write(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $row1['body']['$permissions']); + $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row1['body']['$permissions']); + $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row1['body']['$permissions']); + + /** + * Test for FAILURE + */ + + // Document does not allow create permission + $row2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + ], + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertEquals(400, $row2['headers']['status-code']); + } + + public function testUpdateWithoutPermission(): array + { + // If document has been created by server and client tried to update it without adjusting permissions, permission validation should be skipped + + // As a part of preparation, we get ID of currently logged-in user + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(200, $response['headers']['status-code']); + + $userId = $response['body']['$id']; + + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::custom('permissionCheckDatabase'), + 'name' => 'Test Database', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('Test Database', $database['body']['name']); + + $databaseId = $database['body']['$id']; + // Create collection + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('permissionCheck'), + 'name' => 'permissionCheck', + 'permissions' => [], + 'documentSecurity' => true, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + // Add attribute to collection + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/permissionCheck/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ]); + $this->assertEquals(202, $response['headers']['status-code']); + + // Wait for database worker to finish creating attributes + sleep(2); + + // Creating document by server, give read permission to our user + some other user + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/permissionCheck/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => ID::custom('permissionCheckDocument'), + 'data' => [ + 'name' => 'AppwriteBeginner', + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom('user2'))), + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ], + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Update document + // This is the point of this test. We should be allowed to do this action, and it should not fail on permission check + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/permissionCheck/rows/permissionCheckDocument', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'name' => 'AppwriteExpert', + ] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Get name of the document, should be the new one + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/permissionCheck/rows/permissionCheckDocument', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals("AppwriteExpert", $response['body']['name']); + + // Cleanup to prevent collision with other tests + // Delete collection + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/permissionCheck', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + + + // Wait for database worker to finish deleting collection + sleep(2); + + // Make sure collection has been deleted + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/permissionCheck', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + $this->assertEquals(404, $response['headers']['status-code']); + + return []; + } + + public function testUpdateTwoWayRelationship(): void + { + + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Test Database' + ]); + + $databaseId = $database['body']['$id']; + + + // Creating collection 1 + $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'level1', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + // Creating collection 2 + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'level2', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + \sleep(2); + + // Creating two way relationship between collection 1 and collection 2 from collection 1 + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => 'oneToMany', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => $table2['body']['$id'], + 'twoWayKey' => $table1['body']['$id'] + ]); + + \sleep(3); + + // Update relation from collection 2 to on delete restrict + $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table2['body']['$id'] . '/columns/' . $table1['body']['$id'] . '/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'onDelete' => 'restrict', + ]); + + // Fetching attributes after updating relation to compare + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + + $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + $this->assertEquals('restrict', $table1RelationAttribute['onDelete']); + } + + public function testRelationshipSameTwoWayKey(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Same two way key' + ]); + + $databaseId = $database['body']['$id']; + + $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'c1', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'c2', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + \sleep(2); + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => Database::RELATION_ONE_TO_ONE, + 'twoWay' => false, + 'onDelete' => 'cascade', + 'key' => 'attr1', + 'twoWayKey' => 'same_key' + ]); + + \sleep(2); + + $this->assertEquals(202, $relation['headers']['status-code']); + $this->assertEquals('same_key', $relation['body']['twoWayKey']); + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => Database::RELATION_ONE_TO_MANY, + 'twoWay' => false, + 'onDelete' => 'cascade', + 'key' => 'attr2', + 'twoWayKey' => 'same_key' + ]); + + \sleep(2); + + $this->assertEquals(409, $relation['body']['code']); + $this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']); + + // twoWayKey is null TwoWayKey is default + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => Database::RELATION_ONE_TO_MANY, + 'twoWay' => false, + 'onDelete' => 'cascade', + 'key' => 'attr3', + ]); + + \sleep(2); + + $this->assertEquals(202, $relation['headers']['status-code']); + $this->assertArrayHasKey('twoWayKey', $relation['body']); + + // twoWayKey is null, TwoWayKey is default, second POST + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => Database::RELATION_ONE_TO_MANY, + 'twoWay' => false, + 'onDelete' => 'cascade', + 'key' => 'attr4', + ]); + + \sleep(2); + + $this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']); + $this->assertEquals(409, $relation['body']['code']); + + // RelationshipManyToMany + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => Database::RELATION_MANY_TO_MANY, + 'twoWay' => true, + 'onDelete' => 'setNull', + 'key' => 'songs', + 'twoWayKey' => 'playlist', + ]); + + \sleep(2); + + $this->assertEquals(202, $relation['headers']['status-code']); + $this->assertArrayHasKey('twoWayKey', $relation['body']); + + // Second RelationshipManyToMany on Same collections + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => Database::RELATION_MANY_TO_MANY, + 'twoWay' => true, + 'onDelete' => 'setNull', + 'key' => 'songs2', + 'twoWayKey' => 'playlist2', + ]); + + \sleep(2); + + $this->assertEquals(409, $relation['body']['code']); + $this->assertEquals('Creating more than one "manyToMany" relationship on the same table is currently not permitted.', $relation['body']['message']); + } + + public function testUpdateWithoutRelationPermission(): void + { + $userId = $this->getUser()['$id']; + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => ID::unique(), + ]); + + $databaseId = $database['body']['$id']; + + // Creating collection 1 + $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection1'), + 'name' => ID::custom('collection1'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($userId)), + Permission::read(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ]); + + // Creating collection 2 + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection2'), + 'name' => ID::custom('collection2'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::read(Role::user($userId)), + ] + ]); + + $table3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection3'), + 'name' => ID::custom('collection3'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($userId)), + Permission::read(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ]); + + $table4 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection4'), + 'name' => ID::custom('collection4'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::read(Role::user($userId)), + ] + ]); + + $table5 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection5'), + 'name' => ID::custom('collection5'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($userId)), + Permission::read(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ]); + + // Creating one to one relationship from collection 1 to colletion 2 + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2['body']['$id'], + 'type' => 'oneToOne', + 'twoWay' => false, + 'onDelete' => 'setNull', + 'key' => $table2['body']['$id'] + ]); + + // Creating one to one relationship from collection 2 to colletion 3 + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table2['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table3['body']['$id'], + 'type' => 'oneToOne', + 'twoWay' => false, + 'onDelete' => 'setNull', + 'key' => $table3['body']['$id'] + ]); + + // Creating one to one relationship from collection 3 to colletion 4 + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table3['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table4['body']['$id'], + 'type' => 'oneToOne', + 'twoWay' => false, + 'onDelete' => 'setNull', + 'key' => $table4['body']['$id'] + ]); + + // Creating one to one relationship from collection 4 to colletion 5 + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table4['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table5['body']['$id'], + 'type' => 'oneToOne', + 'twoWay' => false, + 'onDelete' => 'setNull', + 'key' => $table5['body']['$id'] + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "Title", + 'size' => 100, + 'required' => false, + 'array' => false, + 'default' => null, + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table2['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "Rating", + 'size' => 100, + 'required' => false, + 'array' => false, + 'default' => null, + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table3['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "Rating", + 'size' => 100, + 'required' => false, + 'array' => false, + 'default' => null, + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table4['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "Rating", + 'size' => 100, + 'required' => false, + 'array' => false, + 'default' => null, + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table5['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "Rating", + 'size' => 100, + 'required' => false, + 'array' => false, + 'default' => null, + ]); + + \sleep(2); + // Creating parent document with a child reference to test the permissions + $parentDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => ID::custom($table1['body']['$id']), + 'data' => [ + 'Title' => 'Captain America', + $table2['body']['$id'] => [ + '$id' => ID::custom($table2['body']['$id']), + 'Rating' => '10', + $table3['body']['$id'] => [ + '$id' => ID::custom($table3['body']['$id']), + 'Rating' => '10', + $table4['body']['$id'] => [ + '$id' => ID::custom($table4['body']['$id']), + 'Rating' => '10', + $table5['body']['$id'] => [ + '$id' => ID::custom($table5['body']['$id']), + 'Rating' => '10' + ] + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $parentDocument['headers']['status-code']); + // This is the point of the test. We should not need any authorization permission to update the document with same data. + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/rows/' . $table1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::custom($table1['body']['$id']), + 'data' => [ + 'Title' => 'Captain America', + $table2['body']['$id'] => [ + '$id' => $table2['body']['$id'], + 'Rating' => '10', + $table3['body']['$id'] => [ + '$id' => $table3['body']['$id'], + 'Rating' => '10', + $table4['body']['$id'] => [ + '$id' => $table4['body']['$id'], + 'Rating' => '10', + $table5['body']['$id'] => [ + '$id' => $table5['body']['$id'], + 'Rating' => '10' + ] + ] + ] + ] + ] + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($parentDocument['body'], $response['body']); + + // Giving update permission of collection 3 to user. + $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/collection3', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection3'), + 'name' => ID::custom('collection3'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($userId)), + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ]); + + // This is the point of this test. We should be allowed to do this action, and it should not fail on permission check + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/rows/' . $table1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'Title' => 'Captain America', + $table2['body']['$id'] => [ + '$id' => ID::custom($table2['body']['$id']), + 'Rating' => '10', + $table3['body']['$id'] => [ + '$id' => ID::custom($table3['body']['$id']), + 'Rating' => '11', + $table4['body']['$id'] => [ + '$id' => ID::custom($table4['body']['$id']), + 'Rating' => '10', + $table5['body']['$id'] => [ + '$id' => ID::custom($table5['body']['$id']), + 'Rating' => '11' + ] + ] + ] + ] + ] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(11, $response['body'][$table2['body']['$id']]['collection3']['Rating']); + + // We should not be allowed to update the document as we do not have permission for collection 2. + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/rows/' . $table1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'Title' => 'Captain America', + $table2['body']['$id'] => [ + '$id' => ID::custom($table2['body']['$id']), + 'Rating' => '11', + $table3['body']['$id'] => null, + ] + ] + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + + // We should not be allowed to update the document as we do not have permission for collection 2. + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table2['body']['$id'] . '/rows/' . $table2['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'Rating' => '11', + ] + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + + // Removing update permission from collection 3. + $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/collection3', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection3'), + 'name' => ID::custom('collection3'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($userId)), + Permission::read(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ]); + + // Giving update permission to collection 2. + $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/collection2', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('collection2'), + 'name' => ID::custom('collection2'), + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::read(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ]); + + // Creating collection 3 new document + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table3['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => ID::custom('collection3Doc1'), + 'data' => [ + 'Rating' => '20' + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // We should be allowed to link a new document from collection 3 to collection 2. + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/rows/' . $table1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'Title' => 'Captain America', + $table2['body']['$id'] => [ + '$id' => ID::custom($table2['body']['$id']), + $table3['body']['$id'] => 'collection3Doc1', + ] + ] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + + // We should be allowed to link and create a new document from collection 3 to collection 2. + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1['body']['$id'] . '/rows/' . $table1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'Title' => 'Captain America', + $table2['body']['$id'] => [ + '$id' => ID::custom($table2['body']['$id']), + $table3['body']['$id'] => [ + '$id' => ID::custom('collection3Doc2') + ], + ] + ] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php new file mode 100644 index 0000000000..dca8a16363 --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php @@ -0,0 +1,4113 @@ +client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::custom('first'), + 'name' => 'Test 1', + ]); + + $this->assertEquals(201, $test1['headers']['status-code']); + $this->assertEquals('Test 1', $test1['body']['name']); + + $test2 = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::custom('second'), + 'name' => 'Test 2', + ]); + $this->assertEquals(201, $test2['headers']['status-code']); + $this->assertEquals('Test 2', $test2['body']['name']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(2, $databases['body']['total']); + $this->assertEquals($test1['body']['$id'], $databases['body']['databases'][0]['$id']); + $this->assertEquals($test2['body']['$id'], $databases['body']['databases'][1]['$id']); + + $base = array_reverse($databases['body']['databases']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::limit(1)->toString(), + ], + ]); + $this->assertEquals(200, $databases['headers']['status-code']); + $this->assertCount(1, $databases['body']['databases']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::offset(1)->toString(), + ], + ]); + $this->assertEquals(200, $databases['headers']['status-code']); + $this->assertCount(1, $databases['body']['databases']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('name', ['Test 1', 'Test 2'])->toString(), + ], + ]); + $this->assertEquals(200, $databases['headers']['status-code']); + $this->assertCount(2, $databases['body']['databases']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('name', ['Test 2'])->toString(), + ], + ]); + $this->assertEquals(200, $databases['headers']['status-code']); + $this->assertCount(1, $databases['body']['databases']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('$id', ['first'])->toString(), + ], + ]); + $this->assertEquals(200, $databases['headers']['status-code']); + $this->assertCount(1, $databases['body']['databases']); + + /** + * Test for Order + */ + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderDesc()->toString(), + ], + ]); + + $this->assertEquals(2, $databases['body']['total']); + $this->assertEquals($base[0]['$id'], $databases['body']['databases'][0]['$id']); + $this->assertEquals($base[1]['$id'], $databases['body']['databases'][1]['$id']); + + /** + * Test for After + */ + $base = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['databases'][0]['$id']]))->toString(), + ], + ]); + + $this->assertCount(1, $databases['body']['databases']); + $this->assertEquals($base['body']['databases'][1]['$id'], $databases['body']['databases'][0]['$id']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['databases'][1]['$id']]))->toString(), + ], + ]); + + $this->assertCount(0, $databases['body']['databases']); + $this->assertEmpty($databases['body']['databases']); + + /** + * Test for Before + */ + $base = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['databases'][1]['$id']]))->toString(), + ], + ]); + + $this->assertCount(1, $databases['body']['databases']); + $this->assertEquals($base['body']['databases'][0]['$id'], $databases['body']['databases'][0]['$id']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['databases'][0]['$id']]))->toString(), + ], + ]); + + $this->assertCount(0, $databases['body']['databases']); + $this->assertEmpty($databases['body']['databases']); + + /** + * Test for Search + */ + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'first' + ]); + + $this->assertEquals(1, $databases['body']['total']); + $this->assertEquals('first', $databases['body']['databases'][0]['$id']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Test' + ]); + + $this->assertEquals(2, $databases['body']['total']); + $this->assertEquals('Test 1', $databases['body']['databases'][0]['name']); + $this->assertEquals('Test 2', $databases['body']['databases'][1]['name']); + + $databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Nonexistent' + ]); + + $this->assertEquals(0, $databases['body']['total']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(), + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // This collection already exists + $response = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test 1', + 'databaseId' => ID::custom('first'), + ]); + + $this->assertEquals(409, $response['headers']['status-code']); + return ['databaseId' => $test1['body']['$id']]; + } + + /** + * @depends testListDatabases + */ + public function testGetDatabase(array $data): array + { + $databaseId = $data['databaseId']; + /** + * Test for SUCCESS + */ + $database = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $database['headers']['status-code']); + $this->assertEquals($databaseId, $database['body']['$id']); + $this->assertEquals('Test 1', $database['body']['name']); + $this->assertEquals(true, $database['body']['enabled']); + return ['databaseId' => $database['body']['$id']]; + } + + /** + * @depends testListDatabases + */ + public function testUpdateDatabase(array $data) + { + $databaseId = $data['databaseId']; + + $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'name' => 'Test 1 Updated', + 'enabled' => false, + ]); + + $this->assertEquals(200, $database['headers']['status-code']); + $this->assertEquals('Test 1 Updated', $database['body']['name']); + $this->assertFalse($database['body']['enabled']); + + // Now update the database without the passing the enabled parameter + $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'name' => 'Test 1' + ]); + + $this->assertEquals(200, $database['headers']['status-code']); + $this->assertEquals('Test 1', $database['body']['name']); + $this->assertTrue($database['body']['enabled']); + } + + /** + * @depends testListDatabases + */ + public function testDeleteDatabase($data) + { + $databaseId = $data['databaseId']; + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders())); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEquals("", $response['body']); + + // Try to get the collection and check if it has been deleted + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + public function testListCollections(): array + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'invalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertTrue($database['body']['enabled']); + + $databaseId = $database['body']['$id']; + /** + * Test for SUCCESS + */ + $test1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test 1', + 'tableId' => ID::custom('first'), + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $test2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test 2', + 'tableId' => ID::custom('second'), + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(2, $tables['body']['total']); + $this->assertEquals($test1['body']['$id'], $tables['body']['tables'][0]['$id']); + $this->assertEquals($test1['body']['enabled'], $tables['body']['tables'][0]['enabled']); + $this->assertEquals($test2['body']['$id'], $tables['body']['tables'][1]['$id']); + $this->assertEquals($test1['body']['enabled'], $tables['body']['tables'][0]['enabled']); + + $base = array_reverse($tables['body']['tables']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::limit(1)->toString(), + ], + ]); + + $this->assertEquals(200, $tables['headers']['status-code']); + $this->assertCount(1, $tables['body']['tables']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::offset(1)->toString(), + ], + ]); + + $this->assertEquals(200, $tables['headers']['status-code']); + $this->assertCount(1, $tables['body']['tables']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('enabled', [true])->toString(), + ], + ]); + + $this->assertEquals(200, $tables['headers']['status-code']); + $this->assertCount(2, $tables['body']['tables']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('enabled', [false])->toString(), + ], + ]); + + $this->assertEquals(200, $tables['headers']['status-code']); + $this->assertCount(0, $tables['body']['tables']); + + /** + * Test for Order + */ + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderDesc()->toString(), + ], + ]); + + $this->assertEquals(2, $tables['body']['total']); + $this->assertEquals($base[0]['$id'], $tables['body']['tables'][0]['$id']); + $this->assertEquals($base[1]['$id'], $tables['body']['tables'][1]['$id']); + + /** + * Test for After + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['tables'][0]['$id']]))->toString(), + ], + ]); + + $this->assertCount(1, $tables['body']['tables']); + $this->assertEquals($base['body']['tables'][1]['$id'], $tables['body']['tables'][0]['$id']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => $base['body']['tables'][1]['$id']]))->toString(), + ], + ]); + + $this->assertCount(0, $tables['body']['tables']); + $this->assertEmpty($tables['body']['tables']); + + /** + * Test for Before + */ + $base = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['tables'][1]['$id']]))->toString(), + ], + ]); + + $this->assertCount(1, $tables['body']['tables']); + $this->assertEquals($base['body']['tables'][0]['$id'], $tables['body']['tables'][0]['$id']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorBefore(new Document(['$id' => $base['body']['tables'][0]['$id']]))->toString(), + ], + ]); + + $this->assertCount(0, $tables['body']['tables']); + $this->assertEmpty($tables['body']['tables']); + + /** + * Test for Search + */ + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'first' + ]); + + $this->assertEquals(1, $tables['body']['total']); + $this->assertEquals('first', $tables['body']['tables'][0]['$id']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Test' + ]); + + $this->assertEquals(2, $tables['body']['total']); + $this->assertEquals('Test 1', $tables['body']['tables'][0]['name']); + $this->assertEquals('Test 2', $tables['body']['tables'][1]['name']); + + $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Nonexistent' + ]); + + $this->assertEquals(0, $tables['body']['total']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(), + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // This collection already exists + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test 1', + 'tableId' => ID::custom('first'), + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(409, $response['headers']['status-code']); + return [ + 'databaseId' => $databaseId, + 'tableId' => $test1['body']['$id'], + ]; + } + + /** + * @depends testListCollections + */ + public function testGetCollection(array $data): void + { + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders())); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertEquals('Test 1', $table['body']['name']); + $this->assertEquals('first', $table['body']['$id']); + $this->assertTrue($table['body']['enabled']); + } + + /** + * @depends testListCollections + */ + public function testUpdateCollection(array $data) + { + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $table = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test 1 Updated', + 'enabled' => false + ]); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertEquals('Test 1 Updated', $table['body']['name']); + $this->assertEquals('first', $table['body']['$id']); + $this->assertFalse($table['body']['enabled']); + } + + /** + * @depends testListCollections + */ + public function testCreateEncryptedAttribute(array $data): void + { + + $databaseId = $data['databaseId']; + + /** + * Test for SUCCESS + */ + + // Create collection + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Encrypted Actors Data', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $actors['headers']['status-code']); + $this->assertEquals($actors['body']['name'], 'Encrypted Actors Data'); + + /** + * Test for creating encrypted attributes + */ + + $columnsPath = '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns'; + + $firstName = $this->client->call(Client::METHOD_POST, $columnsPath . '/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'firstName', + 'size' => 256, + 'required' => true, + ]); + + $lastName = $this->client->call(Client::METHOD_POST, $columnsPath . '/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'lastName', + 'size' => 256, + 'required' => true, + 'encrypt' => true, + ]); + + + /** + * Check status of every attribute + */ + $this->assertEquals(202, $firstName['headers']['status-code']); + $this->assertEquals('firstName', $firstName['body']['key']); + $this->assertEquals('string', $firstName['body']['type']); + + $this->assertEquals(202, $lastName['headers']['status-code']); + $this->assertEquals('lastName', $lastName['body']['key']); + $this->assertEquals('string', $lastName['body']['type']); + + // Wait for database worker to finish creating attributes + sleep(2); + + // Creating document to ensure cache is purged on schema change + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => ID::unique(), + 'data' => [ + 'firstName' => 'Jonah', + 'lastName' => 'Jameson', + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + // Check document to ensure cache is purged on schema change + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals('Jonah', $row['body']['firstName']); + $this->assertEquals('Jameson', $row['body']['lastName']); + } + + public function testDeleteAttribute(): array + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'invalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + + $databaseId = $database['body']['$id']; + /** + * Test for SUCCESS + */ + + // Create collection + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Actors', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $actors['headers']['status-code']); + $this->assertEquals($actors['body']['name'], 'Actors'); + + $firstName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'firstName', + 'size' => 256, + 'required' => true, + ]); + + $lastName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'lastName', + 'size' => 256, + 'required' => true, + ]); + + $unneeded = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'unneeded', + 'size' => 256, + 'required' => true, + ]); + + // Wait for database worker to finish creating attributes + sleep(2); + + // Creating document to ensure cache is purged on schema change + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => ID::unique(), + 'data' => [ + 'firstName' => 'lorem', + 'lastName' => 'ipsum', + 'unneeded' => 'dolor' + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'key_lastName', + 'type' => 'key', + 'columns' => [ + 'lastName', + ], + ]); + + // Wait for database worker to finish creating index + sleep(2); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $unneededId = $unneeded['body']['key']; + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertIsArray($table['body']['columns']); + $this->assertCount(3, $table['body']['columns']); + $this->assertEquals($table['body']['columns'][0]['key'], $firstName['body']['key']); + $this->assertEquals($table['body']['columns'][1]['key'], $lastName['body']['key']); + $this->assertEquals($table['body']['columns'][2]['key'], $unneeded['body']['key']); + $this->assertCount(1, $table['body']['indexes']); + $this->assertEquals($table['body']['indexes'][0]['key'], $index['body']['key']); + + // Delete attribute + $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/' . $unneededId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $attribute['headers']['status-code']); + + sleep(2); + + // Check document to ensure cache is purged on schema change + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertNotContains($unneededId, $row['body']); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertIsArray($table['body']['columns']); + $this->assertCount(2, $table['body']['columns']); + $this->assertEquals($table['body']['columns'][0]['key'], $firstName['body']['key']); + $this->assertEquals($table['body']['columns'][1]['key'], $lastName['body']['key']); + + return [ + 'tableId' => $actors['body']['$id'], + 'key' => $index['body']['key'], + 'databaseId' => $databaseId + ]; + } + + /** + * @depends testDeleteAttribute + */ + public function testDeleteIndex($data): array + { + $databaseId = $data['databaseId']; + $index = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/indexes/' . $data['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $index['headers']['status-code']); + + // Wait for database worker to finish deleting index + sleep(2); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['tableId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $this->assertCount(0, $table['body']['indexes']); + + return $data; + } + + /** + * @depends testDeleteIndex + */ + public function testDeleteIndexOnDeleteAttribute($data) + { + $databaseId = $data['databaseId']; + $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'attribute1', + 'size' => 16, + 'required' => true, + ]); + + $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'attribute2', + 'size' => 16, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute1['headers']['status-code']); + $this->assertEquals(202, $attribute2['headers']['status-code']); + $this->assertEquals('attribute1', $attribute1['body']['key']); + $this->assertEquals('attribute2', $attribute2['body']['key']); + + sleep(2); + + $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'index1', + 'type' => 'key', + 'columns' => ['attribute1', 'attribute2'], + 'orders' => ['ASC', 'ASC'], + ]); + + $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'index2', + 'type' => 'key', + 'columns' => ['attribute2'], + ]); + + $this->assertEquals(202, $index1['headers']['status-code']); + $this->assertEquals(202, $index2['headers']['status-code']); + $this->assertEquals('index1', $index1['body']['key']); + $this->assertEquals('index2', $index2['body']['key']); + + sleep(2); + + // Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $attribute2['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $deleted['headers']['status-code']); + + // wait for database worker to complete + sleep(2); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['tableId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertIsArray($table['body']['indexes']); + $this->assertCount(1, $table['body']['indexes']); + $this->assertEquals($index1['body']['key'], $table['body']['indexes'][0]['key']); + $this->assertIsArray($table['body']['indexes'][0]['columns']); + $this->assertCount(1, $table['body']['indexes'][0]['columns']); + $this->assertEquals($attribute1['body']['key'], $table['body']['indexes'][0]['columns'][0]); + + // Delete attribute + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $attribute1['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $deleted['headers']['status-code']); + + return $data; + } + + public function testCleanupDuplicateIndexOnDeleteAttribute() + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'invalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertNotEmpty($table['body']['$id']); + + $tableId = $table['body']['$id']; + + $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'attribute1', + 'size' => 16, + 'required' => true, + ]); + + $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'attribute2', + 'size' => 16, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute1['headers']['status-code']); + $this->assertEquals(202, $attribute2['headers']['status-code']); + $this->assertEquals('attribute1', $attribute1['body']['key']); + $this->assertEquals('attribute2', $attribute2['body']['key']); + + sleep(2); + + $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'index1', + 'type' => 'key', + 'columns' => ['attribute1', 'attribute2'], + 'orders' => ['ASC', 'ASC'], + ]); + + $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'index2', + 'type' => 'key', + 'columns' => ['attribute2'], + ]); + + $this->assertEquals(202, $index1['headers']['status-code']); + $this->assertEquals(202, $index2['headers']['status-code']); + $this->assertEquals('index1', $index1['body']['key']); + $this->assertEquals('index2', $index2['body']['key']); + + sleep(2); + + // Expected behavior: deleting attribute1 would cause index1 to be a duplicate of index2 and automatically removed + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $attribute1['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $deleted['headers']['status-code']); + + // wait for database worker to complete + sleep(2); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertIsArray($table['body']['indexes']); + $this->assertCount(1, $table['body']['indexes']); + $this->assertEquals($index2['body']['key'], $table['body']['indexes'][0]['key']); + $this->assertIsArray($table['body']['indexes'][0]['columns']); + $this->assertCount(1, $table['body']['indexes'][0]['columns']); + $this->assertEquals($attribute2['body']['key'], $table['body']['indexes'][0]['columns'][0]); + + // Delete attribute + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $attribute2['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $deleted['headers']['status-code']); + } + + /** + * @depends testDeleteIndexOnDeleteAttribute + */ + public function testDeleteCollection($data) + { + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + // Add Documents to the collection + $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'firstName' => 'Tom', + 'lastName' => 'Holland', + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], + ]); + + $row2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'firstName' => 'Samuel', + 'lastName' => 'Jackson', + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $row1['headers']['status-code']); + $this->assertIsArray($row1['body']['$permissions']); + $this->assertCount(3, $row1['body']['$permissions']); + $this->assertEquals($row1['body']['firstName'], 'Tom'); + $this->assertEquals($row1['body']['lastName'], 'Holland'); + + $this->assertEquals(201, $row2['headers']['status-code']); + $this->assertIsArray($row2['body']['$permissions']); + $this->assertCount(3, $row2['body']['$permissions']); + $this->assertEquals($row2['body']['firstName'], 'Samuel'); + $this->assertEquals($row2['body']['lastName'], 'Jackson'); + + // Delete the actors collection + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders())); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEquals($response['body'], ""); + + // Try to get the collection and check if it has been deleted + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * @throws Exception + */ + public function testDeleteCollectionDeletesRelatedAttributes(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'databaseId' => ID::unique(), + 'name' => 'TestDeleteCollectionDeletesRelatedAttributes', + ]); + + $databaseId = $database['body']['$id']; + + $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Collection1', + 'documentSecurity' => false, + 'permissions' => [], + ]); + + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Collection2', + 'documentSecurity' => false, + 'permissions' => [], + ]); + + $table1 = $table1['body']['$id']; + $table2 = $table2['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1 . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'relatedTableId' => $table2, + 'type' => Database::RELATION_MANY_TO_ONE, + 'twoWay' => false, + 'key' => 'collection2' + ]); + + sleep(2); + + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $table2, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $this->getHeaders())); + + sleep(2); + + $columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1 . '/columns', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $this->getHeaders())); + + $this->assertEquals(0, $columns['body']['total']); + } + + public function testAttributeRowWidthLimit() + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'invalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('attributeRowWidthLimit'), + 'name' => 'attributeRowWidthLimit', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'attributeRowWidthLimit'); + + $tableId = $table['body']['$id']; + + // Add wide string attributes to approach row width limit + for ($i = 0; $i < 15; $i++) { + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "attribute{$i}", + 'size' => 1024, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + } + + sleep(5); + + $tooWide = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'tooWide', + 'size' => 1024, + 'required' => true, + ]); + + $this->assertEquals(400, $tooWide['headers']['status-code']); + $this->assertEquals('column_limit_exceeded', $tooWide['body']['type']); + } + + public function testIndexLimitException() + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'invalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('testLimitException'), + 'name' => 'testLimitException', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $this->assertEquals('testLimitException', $table['body']['name']); + + $tableId = $table['body']['$id']; + + // add unique attributes for indexing + for ($i = 0; $i < 64; $i++) { + // $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "attribute{$i}", + 'size' => 64, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + } + + sleep(10); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'testLimitException'); + $this->assertIsArray($table['body']['columns']); + $this->assertIsArray($table['body']['indexes']); + $this->assertCount(64, $table['body']['columns']); + $this->assertCount(0, $table['body']['indexes']); + + foreach ($table['body']['columns'] as $attribute) { + $this->assertEquals('available', $attribute['status'], 'attribute: ' . $attribute['key']); + } + + // Test indexLimit = 64 + // MariaDB, MySQL, and MongoDB create 6 indexes per new collection + // Add up to the limit, then check if the next index throws IndexLimitException + for ($i = 0; $i < 58; $i++) { + $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => "key_attribute{$i}", + 'type' => 'key', + 'columns' => ["attribute{$i}"], + ]); + + $this->assertEquals(202, $index['headers']['status-code']); + $this->assertEquals("key_attribute{$i}", $index['body']['key']); + } + + sleep(5); + + $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $table['headers']['status-code']); + $this->assertEquals($table['body']['name'], 'testLimitException'); + $this->assertIsArray($table['body']['columns']); + $this->assertIsArray($table['body']['indexes']); + $this->assertCount(64, $table['body']['columns']); + $this->assertCount(58, $table['body']['indexes']); + + $tooMany = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'tooMany', + 'type' => 'key', + 'columns' => ['attribute61'], + ]); + + $this->assertEquals(400, $tooMany['headers']['status-code']); + $this->assertEquals('Index limit exceeded', $tooMany['body']['message']); + + $table = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $table['headers']['status-code']); + } + + public function testAttributeUpdate(): array + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'updateAttributes', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + + $databaseId = $database['body']['$id']; + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::custom('updateAttributes'), + 'name' => 'updateAttributes' + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + + $tableId = $table['body']['$id']; + + /** + * Create String Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'string', + 'size' => 1024, + 'required' => false + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + /** + * Create Email Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'email', + 'required' => false + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + /** + * Create IP Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'ip', + 'required' => false + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + /** + * Create URL Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'url', + 'required' => false + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + /** + * Create Integer Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'integer', + 'required' => false + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + /** + * Create Float Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'float', + 'required' => false + ]); + + /** + * Create Boolean Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'boolean', + 'required' => false + ]); + + /** + * Create Datetime Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'datetime', + 'required' => false + ]); + + /** + * Create Enum Attribute + */ + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'enum', + 'required' => false, + 'elements' => ['lorem', 'ipsum'] + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + sleep(5); + + return [ + 'databaseId' => $databaseId, + 'tableId' => $tableId + ]; + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateString(array $data) + { + $key = 'string'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'lorem' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('lorem', $new['body']['default']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals('lorem', $attribute['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'ipsum' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('ipsum', $new['body']['default']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'i am no boolean', + 'default' => 'dolor' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => 'ipsum' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => 'ipsum' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateEmail(array $data) + { + $key = 'email'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'torsten@appwrite.io' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('torsten@appwrite.io', $new['body']['default']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals('torsten@appwrite.io', $attribute['default']); + + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'eldad@appwrite.io' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('eldad@appwrite.io', $new['body']['default']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => 'torsten@appwrite.io' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no email' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => 'ipsum' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => 'torsten@appwrite.io' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateIp(array $data) + { + $key = 'ip'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => '127.0.0.1' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('127.0.0.1', $new['body']['default']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals('127.0.0.1', $attribute['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => '192.168.0.1' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('192.168.0.1', $new['body']['default']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => '127.0.0.1' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no ip' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => '127.0.0.1' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => '127.0.0.1' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateUrl(array $data) + { + $key = 'url'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'http://appwrite.io' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('http://appwrite.io', $new['body']['default']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals('http://appwrite.io', $attribute['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'https://appwrite.io' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('https://appwrite.io', $new['body']['default']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => 'https://appwrite.io' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no url' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => 'https://appwrite.io' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => 'https://appwrite.io' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateInteger(array $data) + { + $key = 'integer'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123, + 'min' => 0, + 'max' => 1000 + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals(123, $new['body']['default']); + $this->assertEquals(0, $new['body']['min']); + $this->assertEquals(1000, $new['body']['max']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals(123, $attribute['default']); + $this->assertEquals(0, $attribute['min']); + $this->assertEquals(1000, $attribute['max']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null, + 'min' => 0, + 'max' => 1000 + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + $this->assertEquals(0, $new['body']['min']); + $this->assertEquals(1000, $new['body']['max']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 456, + 'min' => 100, + 'max' => 2000 + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals(456, $new['body']['default']); + $this->assertEquals(100, $new['body']['min']); + $this->assertEquals(2000, $new['body']['max']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 100, + 'min' => 0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 10, + 'max' => 100, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => 123, + 'min' => 0, + 'max' => 500 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no integer', + 'min' => 0, + 'max' => 500 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 100, + 'min' => 'i am no integer', + 'max' => 500 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 100, + 'min' => 0, + 'max' => 'i am no integer' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'min' => 0, + 'max' => 100, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => 50, + 'min' => 0, + 'max' => 100, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => 50, + 'min' => 0, + 'max' => 100 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 50, + 'min' => 55, + 'max' => 100 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 105, + 'min' => 50, + 'max' => 100 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 50, + 'min' => 200, + 'max' => 100 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateFloat(array $data) + { + $key = 'float'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 0.0, + 'max' => 1000.0 + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals(123.456, $new['body']['default']); + $this->assertEquals(0, $new['body']['min']); + $this->assertEquals(1000, $new['body']['max']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals(123.456, $attribute['default']); + $this->assertEquals(0, $attribute['min']); + $this->assertEquals(1000, $attribute['max']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null, + 'min' => 0.0, + 'max' => 1000.0 + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + $this->assertEquals(0, $new['body']['min']); + $this->assertEquals(1000, $new['body']['max']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 456.789, + 'min' => 123.456, + 'max' => 2000.0 + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals(456.789, $new['body']['default']); + $this->assertEquals(123.456, $new['body']['min']); + $this->assertEquals(2000, $new['body']['max']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 0.0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 23.456, + 'max' => 100.0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => 123.456, + 'min' => 0.0, + 'max' => 1000.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no integer', + 'min' => 0.0, + 'max' => 500.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 'i am no integer', + 'max' => 500.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 0.0, + 'max' => 'i am no integer' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'min' => 0.0, + 'max' => 100.0, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => 123.456, + 'min' => 0.0, + 'max' => 100.0, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => 123.456, + 'min' => 0.0, + 'max' => 100.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 200.0, + 'max' => 300.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 0.0, + 'max' => 100.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 50.0, + 'min' => 200.0, + 'max' => 100.0 + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateBoolean(array $data) + { + $key = 'boolean'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => true + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals(true, $new['body']['default']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals(true, $attribute['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => false + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals(false, $new['body']['default']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => true + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no boolean' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => false + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => true + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateDatetime(array $data) + { + $key = 'datetime'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => '1975-06-12 14:12:55+02:00' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('1975-06-12 14:12:55+02:00', $new['body']['default']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals('1975-06-12 14:12:55+02:00', $attribute['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => '1965-06-12 14:12:55+02:00' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('1965-06-12 14:12:55+02:00', $new['body']['default']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => '1975-06-12 14:12:55+02:00' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'i am no datetime' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => '1975-06-12 14:12:55+02:00' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => '1975-06-12 14:12:55+02:00' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateEnum(array $data) + { + $key = 'enum'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'elements' => ['lorem', 'ipsum', 'dolor'], + 'required' => false, + 'default' => 'lorem' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('lorem', $new['body']['default']); + $this->assertCount(3, $new['body']['elements']); + $this->assertContains('lorem', $new['body']['elements']); + $this->assertContains('ipsum', $new['body']['elements']); + $this->assertContains('dolor', $new['body']['elements']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($attribute); + $this->assertFalse($attribute['required']); + $this->assertEquals('lorem', $attribute['default']); + $this->assertCount(3, $attribute['elements']); + $this->assertContains('lorem', $attribute['elements']); + $this->assertContains('ipsum', $attribute['elements']); + $this->assertContains('dolor', $attribute['elements']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'elements' => ['lorem', 'ipsum', 'dolor'], + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertNull($new['body']['default']); + $this->assertCount(3, $new['body']['elements']); + $this->assertContains('lorem', $new['body']['elements']); + $this->assertContains('ipsum', $new['body']['elements']); + $this->assertContains('dolor', $new['body']['elements']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'elements' => ['ipsum', 'dolor'], + 'required' => false, + 'default' => 'dolor' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertFalse($new['body']['required']); + $this->assertEquals('dolor', $new['body']['default']); + $this->assertCount(2, $new['body']['elements']); + $this->assertContains('ipsum', $new['body']['elements']); + $this->assertContains('dolor', $new['body']['elements']); + + /** + * Test against failure + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'elements' => [], + 'required' => false, + 'default' => 'lorem' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'elements' => ['ipsum', 'dolor'], + 'required' => false, + 'default' => 'lorem' + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_VALUE_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => 'no boolean', + 'default' => 'lorem', + 'elements' => ['lorem', 'ipsum', 'dolor'], + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123, + 'elements' => ['lorem', 'ipsum', 'dolor'], + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'lorem', + 'elements' => 'i am no array', + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'lorem', + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'elements' => ['lorem', 'ipsum', 'dolor'], + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => 'lorem', + 'elements' => ['lorem', 'ipsum', 'dolor'], + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => true, + 'default' => 'lorem', + 'elements' => ['lorem', 'ipsum', 'dolor'], + ]); + + $this->assertEquals(400, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_DEFAULT_UNSUPPORTED, $update['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateStringResize(array $data) + { + $key = 'string'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $row = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'string' => 'string' + ], + "permissions" => ["read(\"any\")"] + ] + ); + + // Test Resize Up + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'size' => 2048, + 'default' => '', + 'required' => false + ]); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals(2048, $attribute['body']['size']); + + // Test create new document with new size + $newDoc = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'string' => str_repeat('a', 2048) + ], + "permissions" => ["read(\"any\")"] + ] + ); + + $this->assertEquals(201, $newDoc['headers']['status-code']); + $this->assertEquals(2048, strlen($newDoc['body']['string'])); + + // Test update document with new size + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'string' => str_repeat('a', 2048) + ] + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals(2048, strlen($row['body']['string'])); + + // Test Exception on resize down with data that is too large + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'size' => 10, + 'default' => '', + 'required' => false + ]); + + $this->assertEquals(400, $attribute['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_INVALID_RESIZE, $attribute['body']['type']); + + // original documents to original size, remove new document + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'string' => 'string' + ] + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals('string', $row['body']['string']); + + $deleteDoc = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $newDoc['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $deleteDoc['headers']['status-code']); + + + // Test Resize Down + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'size' => 10, + 'default' => '', + 'required' => false + ]); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals(10, $attribute['body']['size']); + + // Test create new document with new size + $newDoc = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'string' => str_repeat('a', 10) + ], + "permissions" => ["read(\"any\")"] + ] + ); + + $this->assertEquals(201, $newDoc['headers']['status-code']); + $this->assertEquals(10, strlen($newDoc['body']['string'])); + + // Test update document with new size + $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'string' => str_repeat('a', 10) + ] + ]); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals(10, strlen($row['body']['string'])); + + // Try create document with string that is too large + $newDoc = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'string' => str_repeat('a', 11) + ], + "permissions" => ["read(\"any\")"] + ] + ); + + $this->assertEquals(400, $newDoc['headers']['status-code']); + $this->assertEquals(AppwriteException::ROW_INVALID_STRUCTURE, $newDoc['body']['type']); + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeUpdateNotFound(array $data) + { + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + $columns = [ + 'string' => [ + 'required' => false, + 'default' => 'ipsum' + ], + 'email' => [ + 'required' => false, + 'default' => 'eldad@appwrite.io' + ], + 'ip' => [ + 'required' => false, + 'default' => '127.0.0.1' + ], + 'url' => [ + 'required' => false, + 'default' => 'https://appwrite.io' + ], + 'integer' => [ + 'required' => false, + 'default' => 5, + 'min' => 0, + 'max' => 10 + ], + 'float' => [ + 'required' => false, + 'default' => 5.5, + 'min' => 0.0, + 'max' => 10.0 + ], + 'datetime' => [ + 'required' => false, + 'default' => '1975-06-12 14:12:55+02:00' + ], + 'enum' => [ + 'elements' => ['lorem', 'ipsum', 'dolor'], + 'required' => false, + 'default' => 'lorem' + ] + ]; + + foreach ($columns as $key => $payload) { + /** + * Check if Database exists + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/i_dont_exist/tables/' . $tableId . '/columns/' . $key . '/unknown_' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), $payload); + + $this->assertEquals(404, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::DATABASE_NOT_FOUND, $update['body']['type']); + + /** + * Check if Collection exists + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/i_dont_exist/columns/' . $key . '/unknown_' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), $payload); + + $this->assertEquals(404, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::TABLE_NOT_FOUND, $update['body']['type']); + + /** + * Check if Attribute exists + */ + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key . '/unknown_' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), $payload); + + $this->assertEquals(404, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_NOT_FOUND, $update['body']['type']); + } + } + + /** + * @depends testAttributeUpdate + */ + public function testAttributeRename(array $data) + { + $key = 'string'; + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + + // Create document to test against + $row = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'string' => 'string' + ], + "permissions" => ["read(\"any\")"] + ] + ); + + $this->assertEquals(201, $row['headers']['status-code']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 'lorum', + 'newKey' => 'new_string', + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $key = 'new_string'; + + $new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals('new_string', $new['body']['key']); + + $doc1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('new_string', $doc1['body']); + $this->assertEquals('string', $doc1['body']['new_string']); + $this->assertArrayNotHasKey('string', $doc1['body']); + + // Try and create a new document with the new attribute + $doc2 = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'new_string' => 'string' + ], + "permissions" => ["read(\"any\")"] + ] + ); + + $this->assertEquals(201, $doc2['headers']['status-code']); + $this->assertArrayHasKey('new_string', $doc2['body']); + $this->assertEquals('string', $doc2['body']['new_string']); + + // Expect fail, try and create a new document with the old attribute + $doc3 = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), + [ + 'rowId' => 'unique()', + 'data' => [ + 'string' => 'string' + ], + "permissions" => ["read(\"any\")"] + ] + ); + + $this->assertEquals(400, $doc3['headers']['status-code']); + } + + public function createRelationshipCollections() + { + // Prepare the database with collections and relationships + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => 'database1', + 'name' => 'Test Database' + ]); + + $databaseId = $database['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'collection1', + 'name' => 'level1', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'collection2', + 'name' => 'level2', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + \sleep(2); + } + + public function cleanupRelationshipCollection() + { + $this->client->call(Client::METHOD_DELETE, '/databases/database1', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + \sleep(2); + } + + public function testAttributeRenameRelationshipOneToMany() + { + $databaseId = 'database1'; + $table1Id = 'collection1'; + $table2Id = 'collection2'; + + $this->createRelationshipCollections(); + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2Id, + 'type' => 'oneToMany', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + \sleep(3); + + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + + $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + + // Create a document for checking later + $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'unique()', + 'data' => [ + 'level2' => [[ + '$id' => 'unique()', + '$permissions' => ["read(\"any\")"] + ]], + ], + "permissions" => ["read(\"any\")"] + ]); + + $this->assertEquals(201, $originalDocument['headers']['status-code']); + + // Rename the attribute + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'newKey' => 'new_level_2' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + // Check the document's key has been renamed + $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('new_level_2', $newDocument['body']); + $this->assertEquals(1, count($newDocument['body']['new_level_2'])); + $this->assertArrayNotHasKey('level2', $newDocument['body']); + + // Check level2 document has been renamed + $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2'][0]['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('level1', $level2Document['body']); + $this->assertNotEmpty($level2Document['body']['level1']); + + // Check if attribute was renamed on the parent's side + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table1Attributes['headers']['status-code']); + $this->assertEquals(1, count($table1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + + // Check if attribute was renamed on the child's side + $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table2Attributes['headers']['status-code']); + $this->assertEquals(1, count($table2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + + $this->cleanupRelationshipCollection(); + } + + public function testAttributeRenameRelationshipOneToOne() + { + $databaseId = 'database1'; + $table1Id = 'collection1'; + $table2Id = 'collection2'; + + $this->createRelationshipCollections(); + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2Id, + 'type' => 'oneToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + \sleep(3); + + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + + $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + + // Create a document for checking later + $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'unique()', + 'data' => [ + 'level2' => [ + '$id' => 'unique()', + '$permissions' => ["read(\"any\")"] + ], + ], + "permissions" => ["read(\"any\")"] + ]); + + $this->assertEquals(201, $originalDocument['headers']['status-code']); + + // Rename the attribute + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'newKey' => 'new_level_2' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + // Check the document's key has been renamed + $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('new_level_2', $newDocument['body']); + $this->assertNotEmpty($newDocument['body']['new_level_2']); + $this->assertArrayNotHasKey('level2', $newDocument['body']); + + // Check level2 document has been renamed + $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('level1', $level2Document['body']); + $this->assertNotEmpty($level2Document['body']['level1']); + + // Check if attribute was renamed on the parent's side + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table1Attributes['headers']['status-code']); + $this->assertEquals(1, count($table1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + + // Check if attribute was renamed on the child's side + $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table2Attributes['headers']['status-code']); + $this->assertEquals(1, count($table2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + + $this->cleanupRelationshipCollection(); + } + + public function testAttributeRenameRelationshipManyToOne() + { + $databaseId = 'database1'; + $table1Id = 'collection1'; + $table2Id = 'collection2'; + + $this->createRelationshipCollections(); + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2Id, + 'type' => 'manyToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + \sleep(3); + + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + + $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + + // Create a document for checking later + $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'unique()', + 'data' => [ + 'level2' => [ + '$id' => 'unique()', + '$permissions' => ["read(\"any\")"] + ], + ], + "permissions" => ["read(\"any\")"] + ]); + + $this->assertEquals(201, $originalDocument['headers']['status-code']); + + // Rename the attribute + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'newKey' => 'new_level_2' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + // Check the document's key has been renamed + $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('new_level_2', $newDocument['body']); + $this->assertNotEmpty($newDocument['body']['new_level_2']); + $this->assertArrayNotHasKey('level2', $newDocument['body']); + + // Check level2 document has been renamed + $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('level1', $level2Document['body']); + $this->assertNotEmpty($level2Document['body']['level1']); + + // Check if attribute was renamed on the parent's side + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table1Attributes['headers']['status-code']); + $this->assertEquals(1, count($table1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + + // Check if attribute was renamed on the child's side + $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table2Attributes['headers']['status-code']); + $this->assertEquals(1, count($table2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + + $this->cleanupRelationshipCollection(); + } + + public function testAttributeRenameRelationshipManyToMany() + { + $databaseId = 'database1'; + $table1Id = 'collection1'; + $table2Id = 'collection2'; + + $this->createRelationshipCollections(); + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedTableId' => $table2Id, + 'type' => 'manyToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + \sleep(3); + + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + + $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + + // Create a document for checking later + $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'unique()', + 'data' => [ + 'level2' => [ + '$id' => 'unique()', + '$permissions' => ["read(\"any\")"] + ], + ], + "permissions" => ["read(\"any\")"] + ]); + + $this->assertEquals(201, $originalDocument['headers']['status-code']); + + // Rename the attribute + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'newKey' => 'new_level_2' + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + // Check the document's key has been renamed + $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('new_level_2', $newDocument['body']); + $this->assertNotEmpty($newDocument['body']['new_level_2']); + $this->assertArrayNotHasKey('level2', $newDocument['body']); + + // Check level2 document has been renamed + $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertArrayHasKey('level1', $level2Document['body']); + $this->assertNotEmpty($level2Document['body']['level1']); + + // Check if attribute was renamed on the parent's side + $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table1Attributes['headers']['status-code']); + $this->assertEquals(1, count($table1Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + + // Check if attribute was renamed on the child's side + $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + + $this->assertEquals(200, $table2Attributes['headers']['status-code']); + $this->assertEquals(1, count($table2Attributes['body']['columns'])); + $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + + $this->cleanupRelationshipCollection(); + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php new file mode 100644 index 0000000000..fa04ba04aa --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php @@ -0,0 +1,278 @@ +client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'InvalidDocumentDatabase', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('InvalidDocumentDatabase', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $publicMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $privateMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [], + 'documentSecurity' => true, + ]); + + $publicCollection = ['id' => $publicMovies['body']['$id']]; + $privateCollection = ['id' => $privateMovies['body']['$id']]; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicCollection['id'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollection['id'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + return [ + 'databaseId' => $databaseId, + 'publicCollectionId' => $publicCollection['id'], + 'privateCollectionId' => $privateCollection['id'], + ]; + } + + public function permissionsProvider(): array + { + return [ + [[Permission::read(Role::any())]], + [[Permission::read(Role::users())]], + [[Permission::update(Role::any()), Permission::delete(Role::any())]], + [[Permission::read(Role::any()), Permission::update(Role::any()), Permission::delete(Role::any())]], + [[Permission::read(Role::users()), Permission::update(Role::users()), Permission::delete(Role::users())]], + [[Permission::read(Role::any()), Permission::update(Role::users()), Permission::delete(Role::users())]], + ]; + } + + /** + * @dataProvider permissionsProvider + */ + public function testReadDocuments($permissions) + { + $data = $this->createCollection(); + $publicCollectionId = $data['publicCollectionId']; + $privateCollectionId = $data['privateCollectionId']; + $databaseId = $data['databaseId']; + + $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + 'permissions' => $permissions, + ]); + $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + 'permissions' => $permissions, + ]); + + $this->assertEquals(201, $publicResponse['headers']['status-code']); + $this->assertEquals(201, $privateResponse['headers']['status-code']); + + $roles = Authorization::getRoles(); + Authorization::cleanRoles(); + + $publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + $privateDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + $this->assertEquals(1, $publicDocuments['body']['total']); + $this->assertEquals($permissions, $publicDocuments['body']['rows'][0]['$permissions']); + + if (\in_array(Permission::read(Role::any()), $permissions)) { + $this->assertEquals(1, $privateDocuments['body']['total']); + $this->assertEquals($permissions, $privateDocuments['body']['rows'][0]['$permissions']); + } else { + $this->assertEquals(0, $privateDocuments['body']['total']); + } + + foreach ($roles as $role) { + Authorization::setRole($role); + } + } + + public function testWriteDocument() + { + $data = $this->createCollection(); + $publicCollectionId = $data['publicCollectionId']; + $privateCollectionId = $data['privateCollectionId']; + $databaseId = $data['databaseId']; + + $roles = Authorization::getRoles(); + Authorization::cleanRoles(); + + $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ] + ]); + + $publicDocumentId = $publicResponse['body']['$id']; + $this->assertEquals(201, $publicResponse['headers']['status-code']); + + $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + ]); + + $this->assertEquals(401, $privateResponse['headers']['status-code']); + + // Create a document in private collection with API key so we can test that update and delete are also not allowed + $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + ]); + + $this->assertEquals(201, $privateResponse['headers']['status-code']); + $privateDocumentId = $privateResponse['body']['$id']; + + $publicDocument = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows/' . $publicDocumentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + ]); + + $this->assertEquals(200, $publicDocument['headers']['status-code']); + $this->assertEquals('Thor: Ragnarok', $publicDocument['body']['title']); + + $privateDocument = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows/' . $privateDocumentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + ]); + + $this->assertEquals(401, $privateDocument['headers']['status-code']); + + $publicDocument = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows/' . $publicDocumentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + $this->assertEquals(204, $publicDocument['headers']['status-code']); + + $privateDocument = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows/' . $privateDocumentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + $this->assertEquals(401, $privateDocument['headers']['status-code']); + + foreach ($roles as $role) { + Authorization::setRole($role); + } + } + + public function testWriteDocumentWithPermissions() + { + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'GuestPermissionsWrite', + ]); + $this->assertEquals(201, $database['headers']['status-code']); + $this->assertEquals('GuestPermissionsWrite', $database['body']['name']); + + $databaseId = $database['body']['$id']; + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::create(Role::any()), + ], + 'documentSecurity' => true + ]); + + $moviesId = $movies['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + sleep(1); + + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Thor: Ragnarok', + ], + 'permissions' => [ + Permission::read(Role::any()), + ] + ]); + + $this->assertEquals(201, $row['headers']['status-code']); + $this->assertEquals('Thor: Ragnarok', $row['body']['title']); + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php new file mode 100644 index 0000000000..8da1d9e1ed --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php @@ -0,0 +1,271 @@ + $this->createUser('user1', 'lorem@ipsum.com'), + 'user2' => $this->createUser('user2', 'dolor@ipsum.com'), + ]; + } + + public function permissionsProvider(): array + { + return [ + [ + 'permissions' => [Permission::read(Role::any())], + 'any' => 1, + 'users' => 1, + 'doconly' => 1, + ], + [ + 'permissions' => [Permission::read(Role::users())], + 'any' => 2, + 'users' => 2, + 'doconly' => 2, + ], + [ + 'permissions' => [Permission::read(Role::user(ID::custom('random')))], + 'any' => 3, + 'users' => 3, + 'doconly' => 2, + ], + [ + 'permissions' => [Permission::read(Role::user(ID::custom('lorem'))), Permission::update(Role::user('lorem')), Permission::delete(Role::user('lorem'))], + 'any' => 4, + 'users' => 4, + 'doconly' => 2, + ], + [ + 'permissions' => [Permission::read(Role::user(ID::custom('dolor'))), Permission::update(Role::user('dolor')), Permission::delete(Role::user('dolor'))], + 'any' => 5, + 'users' => 5, + 'doconly' => 2, + ], + [ + 'permissions' => [Permission::read(Role::user(ID::custom('dolor'))), Permission::read(Role::user('lorem')), Permission::update(Role::user('dolor')), Permission::delete(Role::user('dolor'))], + 'any' => 6, + 'users' => 6, + 'doconly' => 2, + ], + [ + 'permissions' => [Permission::update(Role::any()), Permission::delete(Role::any())], + 'any' => 7, + 'users' => 7, + 'doconly' => 2, + ], + [ + 'permissions' => [Permission::read(Role::any()), Permission::update(Role::any()), Permission::delete(Role::any())], + 'any' => 8, + 'users' => 8, + 'doconly' => 3, + ], + [ + 'permissions' => [Permission::read(Role::any()), Permission::update(Role::users()), Permission::delete(Role::users())], + 'any' => 9, + 'users' => 9, + 'doconly' => 4, + ], + [ + 'permissions' => [Permission::read(Role::user(ID::custom('user1')))], + 'any' => 10, + 'users' => 10, + 'doconly' => 5, + ], + [ + 'permissions' => [Permission::read(Role::user(ID::custom('user1'))), Permission::read(Role::user(ID::custom('user1')))], + 'any' => 11, + 'users' => 11, + 'doconly' => 6, + ], + [ + 'permissions' => [Permission::read(Role::users()), Permission::update(Role::users()), Permission::delete(Role::users())], + 'any' => 12, + 'users' => 12, + 'doconly' => 7, + ], + ]; + } + + /** + * Setup database + * + * Data providers lose object state so explicitly pass [$users, $tables] to each iteration + * + * @return array + * @throws \Exception + */ + public function testSetupDatabase(): array + { + $this->createUsers(); + + $db = $this->client->call(Client::METHOD_POST, '/databases', $this->getServerHeader(), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Database', + ]); + $this->assertEquals(201, $db['headers']['status-code']); + + $databaseId = $db['body']['$id']; + + $public = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + $this->assertEquals(201, $public['headers']['status-code']); + $this->collections = ['public' => $public['body']['$id']]; + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->collections['public'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(202, $response['headers']['status-code']); + + $private = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::unique(), + 'name' => 'Private Movies', + 'permissions' => [ + Permission::read(Role::users()), + Permission::create(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + 'documentSecurity' => true, + ]); + $this->assertEquals(201, $private['headers']['status-code']); + $this->collections['private'] = $private['body']['$id']; + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->collections['private'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(202, $response['headers']['status-code']); + + $doconly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::unique(), + 'name' => 'Document Only Movies', + 'permissions' => [], + 'documentSecurity' => true, + ]); + $this->assertEquals(201, $private['headers']['status-code']); + $this->collections['doconly'] = $doconly['body']['$id']; + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->collections['doconly'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(202, $response['headers']['status-code']); + + sleep(2); + + return [ + 'users' => $this->users, + 'tables' => $this->collections, + 'databaseId' => $databaseId + ]; + } + + /** + * Data provider params are passed before test dependencies + * @dataProvider permissionsProvider + * @depends testSetupDatabase + */ + public function testReadDocuments($permissions, $anyCount, $usersCount, $docOnlyCount, $data) + { + $users = $data['users']; + $tables = $data['tables']; + $databaseId = $data['databaseId']; + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tables['public'] . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + 'permissions' => $permissions + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tables['private'] . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + 'permissions' => $permissions + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tables['doconly'] . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + 'permissions' => $permissions + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + /** + * Check "any" permission collection + */ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tables['public'] . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($anyCount, $rows['body']['total']); + + /** + * Check "users" permission collection + */ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tables['private'] . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($usersCount, $rows['body']['total']); + + /** + * Check "user:user1" document only permission collection + */ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tables['doconly'] . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'], + ]); + + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals($docOnlyCount, $rows['body']['total']); + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php new file mode 100644 index 0000000000..a2bc227c8f --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php @@ -0,0 +1,87 @@ +client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? '', + ], [ + 'userId' => $id, + 'email' => $email, + 'password' => $password + ]); + + $this->assertEquals(201, $user['headers']['status-code']); + + $session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $session = $session['cookies']['a_session_' . $this->getProject()['$id']]; + + $user = [ + '$id' => $user['body']['$id'], + 'email' => $user['body']['email'], + 'session' => $session, + ]; + $this->users[$id] = $user; + + return $user; + } + + public function getCreatedUser(string $id): array + { + return $this->users[$id] ?? []; + } + + public function createTeam(string $id, string $name): array + { + $team = $this->client->call(Client::METHOD_POST, '/teams', $this->getServerHeader(), [ + 'teamId' => $id, + 'name' => $name + ]); + $this->teams[$id] = $team['body']; + + return $team['body']; + } + + public function addToTeam(string $user, string $team, array $roles = []): array + { + $membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team . '/memberships', $this->getServerHeader(), [ + 'teamId' => $team, + 'email' => $this->getCreatedUser($user)['email'], + 'roles' => $roles, + 'url' => 'http://localhost:5000/join-us#title' + ]); + + return [ + 'user' => $membership['body']['userId'], + 'membership' => $membership['body']['$id'] + ]; + } + + public function getServerHeader(): array + { + return [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]; + } +} diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php new file mode 100644 index 0000000000..c62583f76c --- /dev/null +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php @@ -0,0 +1,208 @@ + $this->createTeam('team1', 'Team 1'), + 'team2' => $this->createTeam('team2', 'Team 2'), + ]; + } + + public function createUsers(): array + { + return [ + 'user1' => $this->createUser('user1', 'lorem@ipsum.com'), + 'user2' => $this->createUser('user2', 'dolor@ipsum.com'), + 'user3' => $this->createUser('user3', 'sit@ipsum.com'), + ]; + } + + public function createCollections($teams) + { + $db = $this->client->call(Client::METHOD_POST, '/databases', $this->getServerHeader(), [ + 'databaseId' => $this->databaseId, + 'name' => 'Test Database', + ]); + $this->assertEquals(201, $db['headers']['status-code']); + + $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::custom('collection1'), + 'name' => 'Collection 1', + 'permissions' => [ + Permission::read(Role::team($teams['team1']['$id'])), + Permission::create(Role::team($teams['team1']['$id'], 'admin')), + Permission::update(Role::team($teams['team1']['$id'], 'admin')), + Permission::delete(Role::team($teams['team1']['$id'], 'admin')), + ], + ]); + + $this->collections['collection1'] = $table1['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection1'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables', $this->getServerHeader(), [ + 'tableId' => ID::custom('collection2'), + 'name' => 'Collection 2', + 'permissions' => [ + Permission::read(Role::team($teams['team2']['$id'])), + Permission::create(Role::team($teams['team2']['$id'], 'owner')), + Permission::update(Role::team($teams['team2']['$id'], 'owner')), + Permission::delete(Role::team($teams['team2']['$id'], 'owner')), + ] + ]); + + $this->collections['collection2'] = $table2['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection2'] . '/columns/string', $this->getServerHeader(), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + return $this->collections; + } + + /* + * $success = can $user read from $table + * [$user, $table, $success] + */ + public function readDocumentsProvider(): array + { + return [ + ['user1', 'collection1', true], + ['user2', 'collection1', false], + ['user3', 'collection1', true], + ['user1', 'collection2', false], + ['user2', 'collection2', true], + ['user3', 'collection2', true], + ]; + } + + /* + * $success = can $user write to $table + * [$user, $table, $success] + */ + public function writeDocumentsProvider(): array + { + return [ + ['user1', 'collection1', true], + ['user2', 'collection1', false], + ['user3', 'collection1', false], + ['user1', 'collection2', false], + ['user2', 'collection2', true], + ['user3', 'collection2', false], + ]; + } + + /** + * Setup database + * + * Data providers lose object state + * so explicitly pass $users to each iteration + * @return array $users + */ + public function testSetupDatabase(): array + { + $this->createUsers(); + $this->createTeams(); + + $this->addToTeam('user1', 'team1', ['admin']); + $this->addToTeam('user2', 'team2', ['owner']); + + // user3 in both teams but with no roles + $this->addToTeam('user3', 'team1'); + $this->addToTeam('user3', 'team2'); + + $this->createCollections($this->teams); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection1'] . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Lorem', + ], + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection2'] . '/rows', $this->getServerHeader(), [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Ipsum', + ], + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + return $this->users; + } + + /** + * Data provider params are passed before test dependencies + * @depends testSetupDatabase + * @dataProvider readDocumentsProvider + */ + public function testReadDocuments($user, $table, $success, $users) + { + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $this->databaseId . '/tables/' . $table . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'], + ]); + + if ($success) { + $this->assertCount(1, $rows['body']['rows']); + } else { + $this->assertEquals(401, $rows['headers']['status-code']); + } + } + + /** + * @depends testSetupDatabase + * @dataProvider writeDocumentsProvider + */ + public function testWriteDocuments($user, $table, $success, $users) + { + $rows = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $table . '/rows', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'], + ], [ + 'rowId' => ID::unique(), + 'data' => [ + 'title' => 'Ipsum', + ], + ]); + + if ($success) { + $this->assertEquals(201, $rows['headers']['status-code']); + } else { + // 401 if user is a part of team, 404 otherwise + $this->assertContains($rows['headers']['status-code'], [401, 404]); + } + } +} From 241779e0463fea3b5de80debb48ed3b79fb24c8c Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 17:34:41 +0530 Subject: [PATCH 090/173] add: table api tests to abuse. --- tests/e2e/General/AbuseTest.php | 136 ++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 17 deletions(-) diff --git a/tests/e2e/General/AbuseTest.php b/tests/e2e/General/AbuseTest.php index 2ef351f43e..217f9f93ed 100644 --- a/tests/e2e/General/AbuseTest.php +++ b/tests/e2e/General/AbuseTest.php @@ -26,17 +26,115 @@ class AbuseTest extends Scope } } - public function testAbuseCreateDocument() + public function testAbuseCreateDocumentCollectionsAPI() { - $data = $this->createCollection(); + $data = $this->createCollectionOrTable(); $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $max = 120; for ($i = 0; $i <= $max + 1; $i++) { $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'The Hulk ' . $i, + ], + ]); + + if ($i < $max) { + $this->assertEquals(201, $response['headers']['status-code']); + } else { + $this->assertEquals(429, $response['headers']['status-code']); + } + } + } + + public function testAbuseUpdateDocumentCollectionsAPI() + { + $data = $this->createCollectionOrTable(); + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + $max = 120; + + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'The Hulk', + ], + ]); + + $documentId = $document['body']['$id']; + + for ($i = 0; $i <= $max + 1; $i++) { + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'data' => [ + 'title' => 'The Hulk ' . $i, + ], + ]); + + if ($i < $max) { + $this->assertEquals(200, $response['headers']['status-code']); + } else { + $this->assertEquals(429, $response['headers']['status-code']); + } + } + } + + public function testAbuseDeleteDocumentCollectionsAPI() + { + $data = $this->createCollectionOrTable(); + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + $max = 60; + + for ($i = 0; $i <= $max + 1; $i++) { + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'The Hulk', + ], + ]); + + $documentId = $document['body']['$id']; + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + if ($i < $max) { + $this->assertEquals(204, $response['headers']['status-code']); + } else { + $this->assertEquals(429, $response['headers']['status-code']); + } + } + } + + public function testAbuseCreateDocumentTablesAPI() + { + $data = $this->createCollectionOrTable(false); + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + $max = 120; + + for ($i = 0; $i <= $max + 1; $i++) { + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], ], [ 'rowId' => ID::unique(), 'data' => [ @@ -52,14 +150,14 @@ class AbuseTest extends Scope } } - public function testAbuseUpdateDocument() + public function testAbuseUpdateDocumentTablesAPI() { - $data = $this->createCollection(); + $data = $this->createCollectionOrTable(false); $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $max = 120; - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], @@ -90,15 +188,15 @@ class AbuseTest extends Scope } } - public function testAbuseDeleteDocument() + public function testAbuseDeleteDocumentTablesAPI() { - $data = $this->createCollection(); + $data = $this->createCollectionOrTable(false); $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $max = 60; for ($i = 0; $i <= $max + 1; $i++) { - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], @@ -111,7 +209,7 @@ class AbuseTest extends Scope $documentId = $document['body']['$id']; - $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [ + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows/' . $documentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]); @@ -211,7 +309,7 @@ class AbuseTest extends Scope } } - private function createCollection(): array + private function createCollectionOrTable(bool $isCollection = true): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -227,12 +325,16 @@ class AbuseTest extends Scope $databaseId = $database['body']['$id']; - $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [ + $endpoint = $isCollection ? 'collections' : 'tables'; + $idParam = $isCollection ? 'collectionId' : 'tableId'; + $attributePath = $isCollection ? 'attributes' : 'columns'; + + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . "/$endpoint", [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ - 'tableId' => ID::unique(), + $idParam => ID::unique(), 'name' => 'Movies', 'permissions' => [ Permission::read(Role::any()), @@ -244,7 +346,7 @@ class AbuseTest extends Scope $collectionId = $movies['body']['$id']; - $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [ + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . "/$endpoint/" . $collectionId . "/$attributePath/string", [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -258,7 +360,7 @@ class AbuseTest extends Scope return [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, + 'collectionId' => $collectionId, ]; } From d5e94a57be176ffa9dfe3e7fe551c2c9f9c90797 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 17:38:02 +0530 Subject: [PATCH 091/173] revert: graphql tests for collections api. --- tests/e2e/Services/GraphQL/AbuseTest.php | 20 +- tests/e2e/Services/GraphQL/AuthTest.php | 59 ++- tests/e2e/Services/GraphQL/Base.php | 348 +++++++++--------- .../Services/GraphQL/DatabaseClientTest.php | 68 ++-- .../Services/GraphQL/DatabaseServerTest.php | 287 +++++++-------- 5 files changed, 390 insertions(+), 392 deletions(-) diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/AbuseTest.php index 10b8421b04..ea97492c2b 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/AbuseTest.php @@ -30,9 +30,9 @@ class AbuseTest extends Scope { $data = $this->createCollection(); $databaseId = $data['databaseId']; - $collectionId = $data['tableId']; + $collectionId = $data['collectionId']; $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_ROW); + $query = $this->getQuery(self::$CREATE_DOCUMENT); $max = 120; for ($i = 0; $i <= $max + 1; $i++) { @@ -40,8 +40,8 @@ class AbuseTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, - 'rowId' => ID::unique(), + 'collectionId' => $collectionId, + 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', ], @@ -73,7 +73,7 @@ class AbuseTest extends Scope 'password' => 'password', 'databaseId' => 'database', 'databaseName' => 'database', - 'tableId' => 'collection', + 'collectionId' => 'collection', 'collectionName' => 'collection', 'collectionPermissions' => [ Permission::read(Role::users()), @@ -133,12 +133,12 @@ class AbuseTest extends Scope $databaseId = $response['body']['data']['databasesCreate']['_id']; - $query = $this->getQuery(self::$CREATE_TABLE); + $query = $this->getQuery(self::$CREATE_COLLECTION); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $databaseId, - 'tableId' => 'actors', + 'collectionId' => 'actors', 'name' => 'Actors', 'documentSecurity' => false, 'permissions' => [ @@ -156,12 +156,12 @@ class AbuseTest extends Scope $collectionId = $response['body']['data']['databasesCreateCollection']['_id']; - $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, + 'collectionId' => $collectionId, 'key' => 'name', 'size' => 256, 'required' => true, @@ -178,7 +178,7 @@ class AbuseTest extends Scope return [ 'databaseId' => $databaseId, - 'tableId' => $collectionId, + 'collectionId' => $collectionId, ]; } } diff --git a/tests/e2e/Services/GraphQL/AuthTest.php b/tests/e2e/Services/GraphQL/AuthTest.php index 48a2f475e6..ecce29f2b3 100644 --- a/tests/e2e/Services/GraphQL/AuthTest.php +++ b/tests/e2e/Services/GraphQL/AuthTest.php @@ -23,7 +23,7 @@ class AuthTest extends Scope private string $token2; private array $database; - private array $table; + private array $collection; public function setUp(): void { @@ -101,13 +101,13 @@ class AuthTest extends Scope ], $gqlPayload); // Create collection - $query = $this->getQuery(self::$CREATE_TABLE); + $query = $this->getQuery(self::$CREATE_COLLECTION); $userId = $this->account1['body']['data']['accountCreate']['_id']; $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => ID::unique(), + 'collectionId' => ID::unique(), 'name' => 'Actors', 'documentSecurity' => true, 'permissions' => [ @@ -115,19 +115,19 @@ class AuthTest extends Scope ] ] ]; - $this->table = $this->client->call(Client::METHOD_POST, '/graphql', [ + $this->collection = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); // Create string attribute - $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -147,14 +147,14 @@ class AuthTest extends Scope $projectId = $this->getProject()['$id']; // Create document as account 1 - $query = $this->getQuery(self::$CREATE_ROW); + $query = $this->getQuery(self::$CREATE_DOCUMENT); $userId = $this->account1['body']['data']['accountCreate']['_id']; $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'rowId' => ID::unique(), + 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', ], @@ -165,41 +165,40 @@ class AuthTest extends Scope ] ] ]; - - $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + $document = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); // Try to read as account 1 - $query = $this->getQuery(self::$GET_ROW); + $query = $this->getQuery(self::$GET_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'rowId' => $row['body']['data']['databasesCreateRow']['_id'], + 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], ] ]; - $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + $document = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); - $this->assertIsArray($row['body']['data']['databasesGetRow']); - $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($document['body']['data']['databasesGetDocument']); + $this->assertArrayNotHasKey('errors', $document['body']); // Try to read as account 2 - $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + $document = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token2, ], $gqlPayload); - $this->assertArrayHasKey('errors', $row['body']); - $this->assertEquals('Document with the requested ID could not be found.', $row['body']['errors'][0]['message']); + $this->assertArrayHasKey('errors', $document['body']); + $this->assertEquals('Document with the requested ID could not be found.', $document['body']['errors'][0]['message']); } public function testValidAuth() @@ -207,14 +206,14 @@ class AuthTest extends Scope $projectId = $this->getProject()['$id']; // Create document as account 1 - $query = $this->getQuery(self::$CREATE_ROW); + $query = $this->getQuery(self::$CREATE_DOCUMENT); $userId = $this->account1['body']['data']['accountCreate']['_id']; $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'rowId' => ID::unique(), + 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', ], @@ -225,29 +224,29 @@ class AuthTest extends Scope ], ] ]; - $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + $document = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); // Try to delete as account 1 - $query = $this->getQuery(self::$DELETE_ROW); + $query = $this->getQuery(self::$DELETE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], - 'rowId' => $row['body']['data']['databasesCreateRow']['_id'], + 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], + 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], ] ]; - $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + $document = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); - $this->assertIsNotArray($row['body']); - $this->assertEquals(204, $row['headers']['status-code']); + $this->assertIsNotArray($document['body']); + $this->assertEquals(204, $document['headers']['status-code']); } } diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 22112d2e12..121d40156e 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -13,49 +13,49 @@ trait Base public static string $GET_DATABASE = 'get_database'; public static string $UPDATE_DATABASE = 'update_database'; public static string $DELETE_DATABASE = 'delete_database'; - // Tables - public static string $CREATE_TABLE = 'create_table'; - public static string $GET_TABLE = 'get_table'; - public static string $GET_TABLES = 'list_tables'; - public static string $UPDATE_TABLE = 'update_table'; - public static string $DELETE_TABLE = 'delete_table'; - // Columns - public static string $CREATE_STRING_COLUMN = 'create_string_column'; - public static string $CREATE_INTEGER_COLUMN = 'create_integer_column'; - public static string $CREATE_FLOAT_COLUMN = 'create_float_column'; - public static string $CREATE_BOOLEAN_COLUMN = 'create_boolean_column'; - public static string $CREATE_URL_COLUMN = 'create_url_column'; - public static string $CREATE_EMAIL_COLUMN = 'create_email_column'; - public static string $CREATE_IP_COLUMN = 'create_ip_column'; - public static string $CREATE_ENUM_COLUMN = 'create_enum_column'; - public static string $CREATE_DATETIME_COLUMN = 'create_datetime_column'; + // Collections + public static string $CREATE_COLLECTION = 'create_collection'; + public static string $GET_COLLECTION = 'get_collection'; + public static string $GET_COLLECTIONS = 'list_collections'; + public static string $UPDATE_COLLECTION = 'update_collection'; + public static string $DELETE_COLLECTION = 'delete_collection'; + // Attributes + public static string $CREATE_STRING_ATTRIBUTE = 'create_string_attribute'; + public static string $CREATE_INTEGER_ATTRIBUTE = 'create_integer_attribute'; + public static string $CREATE_FLOAT_ATTRIBUTE = 'create_float_attribute'; + public static string $CREATE_BOOLEAN_ATTRIBUTE = 'create_boolean_attribute'; + public static string $CREATE_URL_ATTRIBUTE = 'create_url_attribute'; + public static string $CREATE_EMAIL_ATTRIBUTE = 'create_email_attribute'; + public static string $CREATE_IP_ATTRIBUTE = 'create_ip_attribute'; + public static string $CREATE_ENUM_ATTRIBUTE = 'create_enum_attribute'; + public static string $CREATE_DATETIME_ATTRIBUTE = 'create_datetime_attribute'; - public static string $CREATE_RELATIONSHIP_COLUMN = 'create_relationship_column'; - public static string $UPDATE_STRING_COLUMN = 'update_string_column'; - public static string $UPDATE_INTEGER_COLUMN = 'update_integer_column'; - public static string $UPDATE_FLOAT_COLUMN = 'update_float_column'; - public static string $UPDATE_BOOLEAN_COLUMN = 'update_boolean_column'; - public static string $UPDATE_URL_COLUMN = 'update_url_column'; - public static string $UPDATE_EMAIL_COLUMN = 'update_email_column'; - public static string $UPDATE_IP_COLUMN = 'update_ip_column'; - public static string $UPDATE_ENUM_COLUMN = 'update_enum_column'; - public static string $UPDATE_DATETIME_COLUMN = 'update_datetime_column'; + public static string $CREATE_RELATIONSHIP_ATTRIBUTE = 'create_relationship_attribute'; + public static string $UPDATE_STRING_ATTRIBUTE = 'update_string_attribute'; + public static string $UPDATE_INTEGER_ATTRIBUTE = 'update_integer_attribute'; + public static string $UPDATE_FLOAT_ATTRIBUTE = 'update_float_attribute'; + public static string $UPDATE_BOOLEAN_ATTRIBUTE = 'update_boolean_attribute'; + public static string $UPDATE_URL_ATTRIBUTE = 'update_url_attribute'; + public static string $UPDATE_EMAIL_ATTRIBUTE = 'update_email_attribute'; + public static string $UPDATE_IP_ATTRIBUTE = 'update_ip_attribute'; + public static string $UPDATE_ENUM_ATTRIBUTE = 'update_enum_attribute'; + public static string $UPDATE_DATETIME_ATTRIBUTE = 'update_datetime_attribute'; - public static string $UPDATE_RELATIONSHIP_COLUMN = 'update_relationship_column'; - public static string $GET_COLUMNS = 'get_columns'; - public static string $GET_COLUMN = 'get_column'; - public static string $DELETE_COLUMN = 'delete_column'; + public static string $UPDATE_RELATIONSHIP_ATTRIBUTE = 'update_relationship_attribute'; + public static string $GET_ATTRIBUTES = 'get_attributes'; + public static string $GET_ATTRIBUTE = 'get_attribute'; + public static string $DELETE_ATTRIBUTE = 'delete_attribute'; // Indexes public static string $CREATE_INDEX = 'create_index'; public static string $GET_INDEXES = 'get_indexes'; public static string $GET_INDEX = 'get_index'; public static string $DELETE_INDEX = 'delete_index'; // Documents - public static string $CREATE_ROW = 'create_row_rest'; - public static string $GET_ROWS = 'list_rows'; - public static string $GET_ROW = 'get_row'; - public static string $UPDATE_ROW = 'update_row'; - public static string $DELETE_ROW = 'delete_row'; + public static string $CREATE_DOCUMENT = 'create_document_rest'; + public static string $GET_DOCUMENTS = 'list_documents'; + public static string $GET_DOCUMENT = 'get_document'; + public static string $UPDATE_DOCUMENT = 'update_document'; + public static string $DELETE_DOCUMENT = 'delete_document'; // Custom Entities public static string $CREATE_CUSTOM_ENTITY = 'create_custom_entity'; @@ -258,9 +258,9 @@ trait Base public static string $COMPLEX_QUERY = 'complex_query'; // Fragments - public static string $FRAGMENT_COLUMNS = ' - fragment columnProperties on Columns { - ... on ColumnString { + public static string $FRAGMENT_ATTRIBUTES = ' + fragment attributeProperties on Attributes { + ... on AttributeString { key required array @@ -268,7 +268,7 @@ trait Base default size } - ... on ColumnInteger { + ... on AttributeInteger { key required array @@ -277,7 +277,7 @@ trait Base intMin: min intMax: max } - ... on ColumnFloat { + ... on AttributeFloat { key required array @@ -286,35 +286,35 @@ trait Base floatMin: min floatMax: max } - ... on ColumnBoolean { + ... on AttributeBoolean { key required array status boolDefault:default } - ... on ColumnUrl { + ... on AttributeUrl { key required array status default } - ... on ColumnEmail { + ... on AttributeEmail { key required array status default } - ... on ColumnIp { + ... on AttributeIp { key required array status default } - ... on ColumnEnum { + ... on AttributeEnum { key required array @@ -322,7 +322,7 @@ trait Base default elements } - ... on ColumnDatetime { + ... on AttributeDatetime { key required array @@ -393,20 +393,20 @@ trait Base status } }'; - case self::$GET_TABLE: - return 'query getTable($databaseId: String!, $tableId: String!) { - databasesGetTable(databaseId: $databaseId, tableId: $tableId) { + case self::$GET_COLLECTION: + return 'query getCollection($databaseId: String!, $collectionId: String!) { + databasesGetCollection(databaseId: $databaseId, collectionId: $collectionId) { _id _permissions documentSecurity name } }'; - case self::$GET_TABLES: - return 'query listTables($databaseId: String!) { - databasesListTables(databaseId: $databaseId) { + case self::$GET_COLLECTIONS: + return 'query listCollections($databaseId: String!) { + databasesListCollections(databaseId: $databaseId) { total - tables { + collections { _id _permissions documentSecurity @@ -414,42 +414,42 @@ trait Base } } }'; - case self::$CREATE_TABLE: - return 'mutation createTable($databaseId: String!, $tableId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!]!) { - databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions) { + case self::$CREATE_COLLECTION: + return 'mutation createCollection($databaseId: String!, $collectionId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!]!) { + databasesCreateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions) { _id _permissions documentSecurity name } }'; - case self::$UPDATE_TABLE: - return 'mutation updateTable($databaseId: String!, $tableId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!], $enabled: Boolean){ - databasesUpdateTable(databaseId: $databaseId, tableId: $tableId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions, enabled: $enabled) { + case self::$UPDATE_COLLECTION: + return 'mutation updateCollection($databaseId: String!, $collectionId: String!, $name: String!, $documentSecurity: Boolean!, $permissions: [String!], $enabled: Boolean){ + databasesUpdateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $name, documentSecurity: $documentSecurity, permissions: $permissions, enabled: $enabled) { _id _permissions documentSecurity name } }'; - case self::$DELETE_TABLE: - return 'mutation deleteTable($databaseId: String!, $tableId: String!){ - databasesDeleteTable(databaseId: $databaseId, tableId: $tableId) { + case self::$DELETE_COLLECTION: + return 'mutation deleteCollection($databaseId: String!, $collectionId: String!){ + databasesDeleteCollection(databaseId: $databaseId, collectionId: $collectionId) { status } }'; - case self::$CREATE_STRING_COLUMN: - return 'mutation createStringColumn($databaseId: String!, $tableId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateStringColumn(databaseId: $databaseId, tableId: $tableId, key: $key, size: $size, required: $required, default: $default, array: $array) { + case self::$CREATE_STRING_ATTRIBUTE: + return 'mutation createStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_INTEGER_COLUMN: - return 'mutation createIntegerColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ - databasesCreateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + case self::$CREATE_INTEGER_ATTRIBUTE: + return 'mutation createIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ + databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -458,9 +458,9 @@ trait Base array } }'; - case self::$CREATE_FLOAT_COLUMN: - return 'mutation createFloatColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ - databasesCreateFloatColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + case self::$CREATE_FLOAT_ATTRIBUTE: + return 'mutation createFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ + databasesCreateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -469,45 +469,45 @@ trait Base array } }'; - case self::$CREATE_BOOLEAN_COLUMN: - return 'mutation createBooleanColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ - databasesCreateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_BOOLEAN_ATTRIBUTE: + return 'mutation createBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ + databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_URL_COLUMN: - return 'mutation createUrlColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateUrlColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_URL_ATTRIBUTE: + return 'mutation createUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_EMAIL_COLUMN: - return 'mutation createEmailColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateEmailColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_EMAIL_ATTRIBUTE: + return 'mutation createEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_IP_COLUMN: - return 'mutation createIpColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateIpColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_IP_ATTRIBUTE: + return 'mutation createIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_ENUM_COLUMN: - return 'mutation createEnumColumn($databaseId: String!, $tableId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateEnumColumn(databaseId: $databaseId, tableId: $tableId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { + case self::$CREATE_ENUM_ATTRIBUTE: + return 'mutation createEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { key elements required @@ -515,19 +515,19 @@ trait Base array } }'; - case self::$CREATE_DATETIME_COLUMN: - return 'mutation createDatetimeColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateDatetimeColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + case self::$CREATE_DATETIME_ATTRIBUTE: + return 'mutation createDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + databasesCreateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default array } }'; - case self::$CREATE_RELATIONSHIP_COLUMN: - return 'mutation createRelationshipColumn($databaseId: String!, $tableId: String!, $relatedTableId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ - databasesCreateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, relatedTableId: $relatedTableId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { - relatedTable + case self::$CREATE_RELATIONSHIP_ATTRIBUTE: + return 'mutation createRelationshipAttribute($databaseId: String!, $collectionId: String!, $relatedCollectionId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ + databasesCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { + relatedCollection relationType twoWay key @@ -535,78 +535,78 @@ trait Base onDelete } }'; - case self::$UPDATE_STRING_COLUMN: - return 'mutation updateStringColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateStringColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + case self::$UPDATE_STRING_ATTRIBUTE: + return 'mutation updateStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_INTEGER_COLUMN: - return 'mutation updateIntegerColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ - databasesUpdateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, min: $min, max: $max, default: $default) { + case self::$UPDATE_INTEGER_ATTRIBUTE: + return 'mutation updateIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ + databasesUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) { required min max default } }'; - case self::$UPDATE_FLOAT_COLUMN: - return 'mutation updateFloatColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ - databasesUpdateFloatColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default) { + case self::$UPDATE_FLOAT_ATTRIBUTE: + return 'mutation updateFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ + databasesUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) { required min max default } }'; - case self::$UPDATE_BOOLEAN_COLUMN: - return 'mutation updateBooleanColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: Boolean){ - databasesUpdateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + case self::$UPDATE_BOOLEAN_ATTRIBUTE: + return 'mutation updateBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean){ + databasesUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_URL_COLUMN: - return 'mutation updateUrlColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateUrlColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + case self::$UPDATE_URL_ATTRIBUTE: + return 'mutation updateUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_EMAIL_COLUMN: - return 'mutation updateEmailColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateEmailColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + case self::$UPDATE_EMAIL_ATTRIBUTE: + return 'mutation updateEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_IP_COLUMN: - return 'mutation updateIpColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateIpColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + case self::$UPDATE_IP_ATTRIBUTE: + return 'mutation updateIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_ENUM_COLUMN: - return 'mutation updateEnumColumn($databaseId: String!, $tableId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ - databasesUpdateEnumColumn(databaseId: $databaseId, tableId: $tableId, key: $key, elements: $elements, required: $required, default: $default) { + case self::$UPDATE_ENUM_ATTRIBUTE: + return 'mutation updateEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ + databasesUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) { elements required default } }'; - case self::$UPDATE_DATETIME_COLUMN: - return 'mutation updateDatetimeColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateDatetimeColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + case self::$UPDATE_DATETIME_ATTRIBUTE: + return 'mutation updateDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ + databasesUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; - case self::$UPDATE_RELATIONSHIP_COLUMN: - return 'mutation updateRelationshipColumn($databaseId: String!, $tableId: String!, $key: String!, $onDelete: String){ - databasesUpdateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, key: $key, onDelete: $onDelete) { - relatedTable + case self::$UPDATE_RELATIONSHIP_ATTRIBUTE: + return 'mutation updateRelationshipAttribute($databaseId: String!, $collectionId: String!, $key: String!, $onDelete: String){ + databasesUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) { + relatedCollection relationType twoWay key @@ -615,16 +615,16 @@ trait Base } }'; case self::$CREATE_INDEX: - return 'mutation createIndex($databaseId: String!, $tableId: String!, $key: String!, $type: String!, $columns: [String!]!, $orders: [String!]){ - databasesCreateIndex(databaseId: $databaseId, tableId: $tableId, key: $key, type: $type, columns: $columns, orders: $orders) { + return 'mutation createIndex($databaseId: String!, $collectionId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ + databasesCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { key type status } }'; case self::$GET_INDEXES: - return 'query listIndexes($databaseId: String!, $tableId: String!) { - databasesListIndexes(databaseId: $databaseId, tableId: $tableId) { + return 'query listIndexes($databaseId: String!, $collectionId: String!) { + databasesListIndexes(databaseId: $databaseId, collectionId: $collectionId) { total indexes { key @@ -634,66 +634,66 @@ trait Base } }'; case self::$GET_INDEX: - return 'query getIndex($databaseId: String!, $tableId: String!, $key: String!) { - databasesGetIndex(databaseId: $databaseId, tableId: $tableId, key: $key) { + return 'query getIndex($databaseId: String!, $collectionId: String!, $key: String!) { + databasesGetIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { key type status } }'; case self::$DELETE_INDEX: - return 'mutation deleteIndex($databaseId: String!, $tableId: String!, $key: String!) { - databasesDeleteIndex(databaseId: $databaseId, tableId: $tableId, key: $key) { + return 'mutation deleteIndex($databaseId: String!, $collectionId: String!, $key: String!) { + databasesDeleteIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { status } }'; - case self::$GET_COLUMNS: - return 'query listColumns($databaseId: String!, $tableId: String!) { - databasesListColumns(databaseId: $databaseId, tableId: $tableId) { + case self::$GET_ATTRIBUTES: + return 'query listAttributes($databaseId: String!, $collectionId: String!) { + databasesListAttributes(databaseId: $databaseId, collectionId: $collectionId) { total - columns { - ...columnProperties + attributes { + ...attributeProperties } } - }' . PHP_EOL . self::$FRAGMENT_COLUMNS; - case self::$GET_COLUMN: - return 'query getColumn($databaseId: String!, $tableId: String!, $key: String!) { - databasesGetColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { - ...columnProperties + }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; + case self::$GET_ATTRIBUTE: + return 'query getAttribute($databaseId: String!, $collectionId: String!, $key: String!) { + databasesGetAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + ...attributeProperties } - }' . PHP_EOL . self::$FRAGMENT_COLUMNS; - case self::$DELETE_COLUMN: - return 'mutation deleteColumn($databaseId: String!, $tableId: String!, $key: String!) { - databasesDeleteColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { + }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; + case self::$DELETE_ATTRIBUTE: + return 'mutation deleteAttribute($databaseId: String!, $collectionId: String!, $key: String!) { + databasesDeleteAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { status } }'; - case self::$GET_ROW: - return 'query getRow($databaseId: String!, $tableId: String!, $rowId: String!) { - databasesGetRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { + case self::$GET_DOCUMENT: + return 'query getDocument($databaseId: String!, $collectionId: String!, $documentId: String!) { + databasesGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { _id - _tableId + _collectionId _permissions data } }'; - case self::$GET_ROWS: - return 'query listRows($databaseId: String!, $tableId: String!){ - databasesListRows(databaseId: $databaseId, tableId: $tableId) { + case self::$GET_DOCUMENTS: + return 'query listDocuments($databaseId: String!, $collectionId: String!){ + databasesListDocuments(databaseId: $databaseId, collectionId: $collectionId) { total - rows { + documents { _id - _tableId + _collectionId _permissions data } } }'; - case self::$CREATE_ROW: - return 'mutation createRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]){ - databasesCreateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { + case self::$CREATE_DOCUMENT: + return 'mutation createDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ + databasesCreateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id - _tableId + _collectionId _permissions } }'; @@ -756,17 +756,17 @@ trait Base return 'mutation deleteCustomEntity($id: String!){ actorsDelete(id: $id) }'; - case self::$UPDATE_ROW: - return 'mutation updateRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]){ - databasesUpdateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { + case self::$UPDATE_DOCUMENT: + return 'mutation updateDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ + databasesUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id - _tableId + _collectionId data } }'; - case self::$DELETE_ROW: - return 'mutation deleteRow($databaseId: String!, $tableId: String!, $rowId: String!){ - databasesDeleteRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { + case self::$DELETE_DOCUMENT: + return 'mutation deleteDocument($databaseId: String!, $collectionId: String!, $documentId: String!){ + databasesDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { status } }'; @@ -2246,12 +2246,12 @@ trait Base } }'; case self::$COMPLEX_QUERY: - return 'mutation complex($databaseId: String!, $databaseName: String!, $tableId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) { + return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) { databasesCreate(databaseId: $databaseId, name: $databaseName) { _id name } - databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $collectionName, documentSecurity: $documentSecurity, permissions: $collectionPermissions) { + databasesCreateCollection(databaseId: $databaseId, collectionId: $collectionId, name: $collectionName, documentSecurity: $documentSecurity, permissions: $collectionPermissions) { _id _createdAt _updatedAt @@ -2259,8 +2259,8 @@ trait Base _databaseId name documentSecurity - columns { - ...columnProperties + attributes { + ...attributeProperties } indexes { key @@ -2268,7 +2268,7 @@ trait Base status } } - databasesCreateStringColumn(databaseId: $databaseId, tableId: $tableId, key: "name", size: 255, required: true) { + databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "name", size: 255, required: true) { key type status @@ -2277,7 +2277,7 @@ trait Base default array } - databasesCreateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: "age", min: 0, max: 150, required: true) { + databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "age", min: 0, max: 150, required: true) { key type status @@ -2287,7 +2287,7 @@ trait Base default array } - databasesCreateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: "alive", required: false, default: true) { + databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "alive", required: false, default: true) { key type status @@ -2487,7 +2487,7 @@ trait Base data } } - }' . PHP_EOL . self::$FRAGMENT_COLUMNS; + }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; } throw new \InvalidArgumentException('Invalid query type'); diff --git a/tests/e2e/Services/GraphQL/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/DatabaseClientTest.php index efd2430629..3853a3fc17 100644 --- a/tests/e2e/Services/GraphQL/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseClientTest.php @@ -48,12 +48,12 @@ class DatabaseClientTest extends Scope public function testCreateCollection($database): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_TABLE); + $query = $this->getQuery(self::$CREATE_COLLECTION); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $database['_id'], - 'tableId' => 'actors', + 'collectionId' => 'actors', 'name' => 'Actors', 'documentSecurity' => false, 'permissions' => [ @@ -65,20 +65,20 @@ class DatabaseClientTest extends Scope ] ]; - $table = $this->client->call(Client::METHOD_POST, '/graphql', [ + $collection = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - $this->assertIsArray($table['body']['data']); - $this->assertArrayNotHasKey('errors', $table['body']); - $table = $table['body']['data']['databasesCreateTable']; - $this->assertEquals('Actors', $table['name']); + $this->assertIsArray($collection['body']['data']); + $this->assertArrayNotHasKey('errors', $collection['body']); + $collection = $collection['body']['data']['databasesCreateCollection']; + $this->assertEquals('Actors', $collection['name']); return [ 'database' => $database, - 'table' => $table, + 'collection' => $collection, ]; } @@ -88,12 +88,12 @@ class DatabaseClientTest extends Scope public function testCreateStringAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -108,7 +108,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateStringColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); return $data; } @@ -119,12 +119,12 @@ class DatabaseClientTest extends Scope public function testCreateIntegerAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_INTEGER_COLUMN); + $query = $this->getQuery(self::$CREATE_INTEGER_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'age', 'min' => 18, 'max' => 150, @@ -140,7 +140,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); return $data; } @@ -154,13 +154,13 @@ class DatabaseClientTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_ROW); + $query = $this->getQuery(self::$CREATE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => ID::unique(), + 'collectionId' => $data['collection']['_id'], + 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', 'age' => 35, @@ -181,13 +181,13 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesCreateRow']; + $document = $document['body']['data']['databasesCreateDocument']; $this->assertIsArray($document); return [ 'database' => $data['database'], - 'table' => $data['table'], - 'row' => $document, + 'collection' => $data['collection'], + 'document' => $document, ]; } @@ -198,12 +198,12 @@ class DatabaseClientTest extends Scope public function testGetDocuments($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ROWS); + $query = $this->getQuery(self::$GET_DOCUMENTS); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], ] ]; @@ -214,7 +214,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['databasesListRows']); + $this->assertIsArray($documents['body']['data']['databasesListDocuments']); } /** @@ -224,13 +224,13 @@ class DatabaseClientTest extends Scope public function testGetDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ROW); + $query = $this->getQuery(self::$GET_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => $data['row']['_id'], + 'collectionId' => $data['collection']['_id'], + 'documentId' => $data['document']['_id'], ] ]; @@ -241,7 +241,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['databasesGetRow']); + $this->assertIsArray($document['body']['data']['databasesGetDocument']); } /** @@ -251,13 +251,13 @@ class DatabaseClientTest extends Scope public function testUpdateDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_ROW); + $query = $this->getQuery(self::$UPDATE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => $data['row']['_id'], + 'collectionId' => $data['collection']['_id'], + 'documentId' => $data['document']['_id'], 'data' => [ 'name' => 'New Document Name', ], @@ -271,7 +271,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesUpdateRow']; + $document = $document['body']['data']['databasesUpdateDocument']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); @@ -284,13 +284,13 @@ class DatabaseClientTest extends Scope public function testDeleteDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_ROW); + $query = $this->getQuery(self::$DELETE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => $data['row']['_id'], + 'collectionId' => $data['collection']['_id'], + 'documentId' => $data['document']['_id'], ] ]; diff --git a/tests/e2e/Services/GraphQL/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/DatabaseServerTest.php index 0ea86c03cf..87006a1bea 100644 --- a/tests/e2e/Services/GraphQL/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseServerTest.php @@ -49,12 +49,12 @@ class DatabaseServerTest extends Scope public function testCreateCollection($database): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_TABLE); + $query = $this->getQuery(self::$CREATE_COLLECTION); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $database['_id'], - 'tableId' => 'actors', + 'collectionId' => 'actors', 'name' => 'Actors', 'documentSecurity' => false, 'permissions' => [ @@ -73,14 +73,14 @@ class DatabaseServerTest extends Scope $this->assertIsArray($collection['body']['data']); $this->assertArrayNotHasKey('errors', $collection['body']); - $collection = $collection['body']['data']['databasesCreateTable']; + $collection = $collection['body']['data']['databasesCreateCollection']; $this->assertEquals('Actors', $collection['name']); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $database['_id'], - 'tableId' => 'movies', + 'collectionId' => 'movies', 'name' => 'Movies', 'documentSecurity' => false, 'permissions' => [ @@ -92,20 +92,20 @@ class DatabaseServerTest extends Scope ] ]; - $table2 = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + $collection2 = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); - $this->assertIsArray($table2['body']['data']); - $this->assertArrayNotHasKey('errors', $table2['body']); - $table2 = $table2['body']['data']['databasesCreateTable']; - $this->assertEquals('Movies', $table2['name']); + $this->assertIsArray($collection2['body']['data']); + $this->assertArrayNotHasKey('errors', $collection2['body']); + $collection2 = $collection2['body']['data']['databasesCreateCollection']; + $this->assertEquals('Movies', $collection2['name']); return [ 'database' => $database, - 'table' => $collection, - 'table2' => $table2, + 'collection' => $collection, + 'collection2' => $collection2, ]; } @@ -116,12 +116,12 @@ class DatabaseServerTest extends Scope public function testCreateStringAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -135,7 +135,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateStringColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); return $data; } @@ -150,12 +150,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_STRING_COLUMN); + $query = $this->getQuery(self::$UPDATE_STRING_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'name', 'required' => false, 'default' => 'Default Value', @@ -168,9 +168,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateStringColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateStringColumn']['required']); - $this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateStringAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateStringAttribute']['required']); + $this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -183,12 +183,12 @@ class DatabaseServerTest extends Scope public function testCreateIntegerAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_INTEGER_COLUMN); + $query = $this->getQuery(self::$CREATE_INTEGER_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'age', 'min' => 18, 'max' => 150, @@ -203,7 +203,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); return $data; } @@ -218,12 +218,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_INTEGER_COLUMN); + $query = $this->getQuery(self::$UPDATE_INTEGER_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'age', 'required' => false, 'min' => 12, @@ -238,11 +238,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateIntegerColumn']['required']); - $this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerColumn']['min']); - $this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerColumn']['max']); - $this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateIntegerAttribute']['required']); + $this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerAttribute']['min']); + $this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerAttribute']['max']); + $this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -255,12 +255,12 @@ class DatabaseServerTest extends Scope public function testCreateBooleanAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_BOOLEAN_COLUMN); + $query = $this->getQuery(self::$CREATE_BOOLEAN_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'alive', 'required' => true, ] @@ -273,7 +273,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateBooleanColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateBooleanAttribute']); return $data; } @@ -288,12 +288,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_BOOLEAN_COLUMN); + $query = $this->getQuery(self::$UPDATE_BOOLEAN_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'alive', 'required' => false, 'default' => true @@ -306,9 +306,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateBooleanColumn']['required']); - $this->assertTrue($attribute['body']['data']['databasesUpdateBooleanColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateBooleanAttribute']['required']); + $this->assertTrue($attribute['body']['data']['databasesUpdateBooleanAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -321,12 +321,12 @@ class DatabaseServerTest extends Scope public function testCreateFloatAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_FLOAT_COLUMN); + $query = $this->getQuery(self::$CREATE_FLOAT_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'salary', 'min' => 1000.0, 'max' => 999999.99, @@ -342,7 +342,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateFloatColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateFloatAttribute']); return $data; } @@ -357,12 +357,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_FLOAT_COLUMN); + $query = $this->getQuery(self::$UPDATE_FLOAT_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'salary', 'required' => false, 'min' => 100.0, @@ -377,11 +377,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateFloatColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateFloatColumn']['required']); - $this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatColumn']['min']); - $this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatColumn']['max']); - $this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateFloatAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateFloatAttribute']['required']); + $this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['min']); + $this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['max']); + $this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -394,12 +394,12 @@ class DatabaseServerTest extends Scope public function testCreateEmailAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_EMAIL_COLUMN); + $query = $this->getQuery(self::$CREATE_EMAIL_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'email', 'required' => true, ] @@ -412,7 +412,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateEmailColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateEmailAttribute']); return $data; } @@ -427,12 +427,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_EMAIL_COLUMN); + $query = $this->getQuery(self::$UPDATE_EMAIL_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'email', 'required' => false, 'default' => 'torsten@appwrite.io', @@ -445,9 +445,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateEmailColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateEmailColumn']['required']); - $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateEmailAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateEmailAttribute']['required']); + $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -460,12 +460,12 @@ class DatabaseServerTest extends Scope public function testCreateEnumAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_ENUM_COLUMN); + $query = $this->getQuery(self::$CREATE_ENUM_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'role', 'elements' => [ 'crew', @@ -483,7 +483,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateEnumColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateEnumAttribute']); return $data; } @@ -499,12 +499,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_ENUM_COLUMN); + $query = $this->getQuery(self::$UPDATE_ENUM_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'role', 'required' => false, 'elements' => [ @@ -522,11 +522,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateEnumColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateEnumColumn']['required']); - $this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumColumn']['default']); - $this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumColumn']['elements']); - $this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumColumn']['elements']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateEnumAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateEnumAttribute']['required']); + $this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['default']); + $this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); + $this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -539,12 +539,12 @@ class DatabaseServerTest extends Scope public function testCreateDatetimeAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DATETIME_COLUMN); + $query = $this->getQuery(self::$CREATE_DATETIME_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'dob', 'required' => true, ] @@ -557,7 +557,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateDatetimeColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateDatetimeAttribute']); return $data; } @@ -572,12 +572,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_DATETIME_COLUMN); + $query = $this->getQuery(self::$UPDATE_DATETIME_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'dob', 'required' => false, 'default' => '2000-01-01T00:00:00Z' @@ -590,9 +590,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeColumn']['required']); - $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeAttribute']['required']); + $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -604,13 +604,13 @@ class DatabaseServerTest extends Scope public function testCreateRelationshipAttribute(array $data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_RELATIONSHIP_COLUMN); + $query = $this->getQuery(self::$CREATE_RELATIONSHIP_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table2']['_id'], // Movies - 'relatedTableId' => $data['table']['_id'], // Actors + 'collectionId' => $data['collection2']['_id'], // Movies + 'relatedCollectionId' => $data['collection']['_id'], // Actors 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'actors', @@ -623,10 +623,9 @@ class DatabaseServerTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); - $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipAttribute']); return $data; } @@ -639,12 +638,12 @@ class DatabaseServerTest extends Scope sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_COLUMN); + $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table2']['_id'], + 'collectionId' => $data['collection2']['_id'], 'key' => 'actors', 'onDelete' => Database::RELATION_MUTATE_CASCADE, ] @@ -657,7 +656,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipColumn']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipAttribute']); return $data; } @@ -669,12 +668,12 @@ class DatabaseServerTest extends Scope public function testCreateIPAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_IP_COLUMN); + $query = $this->getQuery(self::$CREATE_IP_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'ip', 'required' => false, 'default' => '::1', @@ -688,7 +687,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIpColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIpAttribute']); return $data; } @@ -703,12 +702,12 @@ class DatabaseServerTest extends Scope sleep(3); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_IP_COLUMN); + $query = $this->getQuery(self::$UPDATE_IP_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'ip', 'required' => false, 'default' => '127.0.0.1' @@ -721,9 +720,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateIpColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateIpColumn']['required']); - $this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateIpAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateIpAttribute']['required']); + $this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -736,12 +735,12 @@ class DatabaseServerTest extends Scope public function testCreateURLAttribute($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_URL_COLUMN); + $query = $this->getQuery(self::$CREATE_URL_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'url', 'required' => false, 'default' => 'https://appwrite.io', @@ -755,7 +754,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateUrlColumn']); + $this->assertIsArray($attribute['body']['data']['databasesCreateUrlAttribute']); return $data; } @@ -770,12 +769,12 @@ class DatabaseServerTest extends Scope sleep(3); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_URL_COLUMN); + $query = $this->getQuery(self::$UPDATE_URL_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'url', 'required' => false, 'default' => 'https://cloud.appwrite.io' @@ -788,9 +787,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateUrlColumn']); - $this->assertFalse($attribute['body']['data']['databasesUpdateUrlColumn']['required']); - $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlColumn']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateUrlAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateUrlAttribute']['required']); + $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); } @@ -807,10 +806,10 @@ class DatabaseServerTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'index', 'type' => 'key', - 'columns' => [ + 'attributes' => [ 'name', 'age', ], @@ -828,7 +827,7 @@ class DatabaseServerTest extends Scope return [ 'database' => $data['database'], - 'table' => $data['table'], + 'collection' => $data['collection'], 'index' => $index['body']['data']['databasesCreateIndex'], ]; } @@ -843,13 +842,13 @@ class DatabaseServerTest extends Scope public function testCreateDocument($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_ROW); + $query = $this->getQuery(self::$CREATE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => ID::unique(), + 'collectionId' => $data['collection']['_id'], + 'documentId' => ID::unique(), 'data' => [ 'name' => 'John Doe', 'email' => 'example@appwrite.io', @@ -867,21 +866,21 @@ class DatabaseServerTest extends Scope ] ]; - $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + $document = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); - $this->assertArrayNotHasKey('errors', $row['body']); - $this->assertIsArray($row['body']['data']); + $this->assertArrayNotHasKey('errors', $document['body']); + $this->assertIsArray($document['body']['data']); - $row = $row['body']['data']['databasesCreateRow']; - $this->assertIsArray($row); + $document = $document['body']['data']['databasesCreateDocument']; + $this->assertIsArray($document); return [ 'database' => $data['database'], - 'table' => $data['table'], - 'row' => $row, + 'collection' => $data['collection'], + 'document' => $document, ]; } @@ -975,7 +974,7 @@ class DatabaseServerTest extends Scope public function testGetCollections($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_TABLES); + $query = $this->getQuery(self::$GET_COLLECTIONS); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -990,7 +989,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $collections['body']); $this->assertIsArray($collections['body']['data']); - $this->assertIsArray($collections['body']['data']['databasesListTables']); + $this->assertIsArray($collections['body']['data']['databasesListCollections']); } /** @@ -1000,12 +999,12 @@ class DatabaseServerTest extends Scope public function testGetCollection($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_TABLE); + $query = $this->getQuery(self::$GET_COLLECTION); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], ] ]; @@ -1016,7 +1015,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $collection['body']); $this->assertIsArray($collection['body']['data']); - $this->assertIsArray($collection['body']['data']['databasesGetTable']); + $this->assertIsArray($collection['body']['data']['databasesGetCollection']); } /** @@ -1027,12 +1026,12 @@ class DatabaseServerTest extends Scope public function testGetAttributes($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_COLUMNS); + $query = $this->getQuery(self::$GET_ATTRIBUTES); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], ] ]; @@ -1043,7 +1042,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attributes['body']); $this->assertIsArray($attributes['body']['data']); - $this->assertIsArray($attributes['body']['data']['databasesListColumns']); + $this->assertIsArray($attributes['body']['data']['databasesListAttributes']); } /** @@ -1053,12 +1052,12 @@ class DatabaseServerTest extends Scope public function testGetAttribute($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_COLUMN); + $query = $this->getQuery(self::$GET_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'name', ] ]; @@ -1070,7 +1069,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesGetColumn']); + $this->assertIsArray($attribute['body']['data']['databasesGetAttribute']); } /** @@ -1085,7 +1084,7 @@ class DatabaseServerTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], ] ]; @@ -1111,7 +1110,7 @@ class DatabaseServerTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => $data['index']['key'], ] ]; @@ -1133,12 +1132,12 @@ class DatabaseServerTest extends Scope public function testGetDocuments($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ROWS); + $query = $this->getQuery(self::$GET_DOCUMENTS); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], ] ]; @@ -1149,7 +1148,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['databasesListRows']); + $this->assertIsArray($documents['body']['data']['databasesListDocuments']); } /** @@ -1159,13 +1158,13 @@ class DatabaseServerTest extends Scope public function testGetDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ROW); + $query = $this->getQuery(self::$GET_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => $data['row']['_id'], + 'collectionId' => $data['collection']['_id'], + 'documentId' => $data['document']['_id'], ] ]; @@ -1176,7 +1175,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['databasesGetRow']); + $this->assertIsArray($document['body']['data']['databasesGetDocument']); } // /** @@ -1259,12 +1258,12 @@ class DatabaseServerTest extends Scope public function testUpdateCollection($data) { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_TABLE); + $query = $this->getQuery(self::$UPDATE_COLLECTION); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'name' => 'New Collection Name', 'documentSecurity' => false, ] @@ -1277,7 +1276,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $collection['body']); $this->assertIsArray($collection['body']['data']); - $this->assertIsArray($collection['body']['data']['databasesUpdateTable']); + $this->assertIsArray($collection['body']['data']['databasesUpdateCollection']); } /** @@ -1287,13 +1286,13 @@ class DatabaseServerTest extends Scope public function testUpdateDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_ROW); + $query = $this->getQuery(self::$UPDATE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => $data['row']['_id'], + 'collectionId' => $data['collection']['_id'], + 'documentId' => $data['document']['_id'], 'data' => [ 'name' => 'New Document Name', ], @@ -1307,7 +1306,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesUpdateRow']; + $document = $document['body']['data']['databasesUpdateDocument']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); } @@ -1347,13 +1346,13 @@ class DatabaseServerTest extends Scope public function testDeleteDocument($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_ROW); + $query = $this->getQuery(self::$DELETE_DOCUMENT); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], - 'rowId' => $data['row']['_id'], + 'collectionId' => $data['collection']['_id'], + 'documentId' => $data['document']['_id'], ] ]; @@ -1397,12 +1396,12 @@ class DatabaseServerTest extends Scope public function testDeleteAttribute($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_COLUMN); + $query = $this->getQuery(self::$DELETE_ATTRIBUTE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], 'key' => 'name', ] ]; @@ -1423,12 +1422,12 @@ class DatabaseServerTest extends Scope public function testDeleteCollection($data) { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_TABLE); + $query = $this->getQuery(self::$DELETE_COLLECTION); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'collectionId' => $data['collection']['_id'], ] ]; From c542ba033f259e8f5c9ff06897666a37d8ba79a6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 18:15:17 +0530 Subject: [PATCH 092/173] patch: graphql tests <> collections. --- tests/e2e/Services/GraphQL/AuthTest.php | 6 +- tests/e2e/Services/GraphQL/Base.php | 64 +++++------ .../Services/GraphQL/DatabaseClientTest.php | 12 +- .../Services/GraphQL/DatabaseServerTest.php | 108 +++++++++--------- 4 files changed, 95 insertions(+), 95 deletions(-) diff --git a/tests/e2e/Services/GraphQL/AuthTest.php b/tests/e2e/Services/GraphQL/AuthTest.php index ecce29f2b3..db56537999 100644 --- a/tests/e2e/Services/GraphQL/AuthTest.php +++ b/tests/e2e/Services/GraphQL/AuthTest.php @@ -178,7 +178,7 @@ class AuthTest extends Scope 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], - 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], + 'documentId' => $document['body']['data']['collectionsCreateDocument']['_id'], ] ]; $document = $this->client->call(Client::METHOD_POST, '/graphql', [ @@ -187,7 +187,7 @@ class AuthTest extends Scope 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); - $this->assertIsArray($document['body']['data']['databasesGetDocument']); + $this->assertIsArray($document['body']['data']['collectionsGetDocument']); $this->assertArrayNotHasKey('errors', $document['body']); // Try to read as account 2 @@ -237,7 +237,7 @@ class AuthTest extends Scope 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], - 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], + 'documentId' => $document['body']['data']['collectionsCreateDocument']['_id'], ] ]; $document = $this->client->call(Client::METHOD_POST, '/graphql', [ diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 121d40156e..f7ee14f7e1 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -440,7 +440,7 @@ trait Base }'; case self::$CREATE_STRING_ATTRIBUTE: return 'mutation createStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { + collectionsCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { key required default @@ -449,7 +449,7 @@ trait Base }'; case self::$CREATE_INTEGER_ATTRIBUTE: return 'mutation createIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ - databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + collectionsCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -460,7 +460,7 @@ trait Base }'; case self::$CREATE_FLOAT_ATTRIBUTE: return 'mutation createFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ - databasesCreateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + collectionsCreateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -471,7 +471,7 @@ trait Base }'; case self::$CREATE_BOOLEAN_ATTRIBUTE: return 'mutation createBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ - databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + collectionsCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -480,7 +480,7 @@ trait Base }'; case self::$CREATE_URL_ATTRIBUTE: return 'mutation createUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + collectionsCreateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -489,7 +489,7 @@ trait Base }'; case self::$CREATE_EMAIL_ATTRIBUTE: return 'mutation createEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + collectionsCreateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -498,7 +498,7 @@ trait Base }'; case self::$CREATE_IP_ATTRIBUTE: return 'mutation createIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + collectionsCreateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -507,7 +507,7 @@ trait Base }'; case self::$CREATE_ENUM_ATTRIBUTE: return 'mutation createEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { + collectionsCreateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { key elements required @@ -517,7 +517,7 @@ trait Base }'; case self::$CREATE_DATETIME_ATTRIBUTE: return 'mutation createDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - databasesCreateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + collectionsCreateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -526,7 +526,7 @@ trait Base }'; case self::$CREATE_RELATIONSHIP_ATTRIBUTE: return 'mutation createRelationshipAttribute($databaseId: String!, $collectionId: String!, $relatedCollectionId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ - databasesCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { + collectionsCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { relatedCollection relationType twoWay @@ -537,14 +537,14 @@ trait Base }'; case self::$UPDATE_STRING_ATTRIBUTE: return 'mutation updateStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + collectionsUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_INTEGER_ATTRIBUTE: return 'mutation updateIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ - databasesUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) { + collectionsUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) { required min max @@ -553,7 +553,7 @@ trait Base }'; case self::$UPDATE_FLOAT_ATTRIBUTE: return 'mutation updateFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ - databasesUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) { + collectionsUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) { required min max @@ -562,35 +562,35 @@ trait Base }'; case self::$UPDATE_BOOLEAN_ATTRIBUTE: return 'mutation updateBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean){ - databasesUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + collectionsUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_URL_ATTRIBUTE: return 'mutation updateUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + collectionsUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_EMAIL_ATTRIBUTE: return 'mutation updateEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + collectionsUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_IP_ATTRIBUTE: return 'mutation updateIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + collectionsUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_ENUM_ATTRIBUTE: return 'mutation updateEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ - databasesUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) { + collectionsUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) { elements required default @@ -598,14 +598,14 @@ trait Base }'; case self::$UPDATE_DATETIME_ATTRIBUTE: return 'mutation updateDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - databasesUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + collectionsUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_RELATIONSHIP_ATTRIBUTE: return 'mutation updateRelationshipAttribute($databaseId: String!, $collectionId: String!, $key: String!, $onDelete: String){ - databasesUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) { + collectionsUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) { relatedCollection relationType twoWay @@ -616,7 +616,7 @@ trait Base }'; case self::$CREATE_INDEX: return 'mutation createIndex($databaseId: String!, $collectionId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ - databasesCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { + collectionsCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { key type status @@ -624,7 +624,7 @@ trait Base }'; case self::$GET_INDEXES: return 'query listIndexes($databaseId: String!, $collectionId: String!) { - databasesListIndexes(databaseId: $databaseId, collectionId: $collectionId) { + collectionsListIndexes(databaseId: $databaseId, collectionId: $collectionId) { total indexes { key @@ -635,7 +635,7 @@ trait Base }'; case self::$GET_INDEX: return 'query getIndex($databaseId: String!, $collectionId: String!, $key: String!) { - databasesGetIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + collectionsGetIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { key type status @@ -643,13 +643,13 @@ trait Base }'; case self::$DELETE_INDEX: return 'mutation deleteIndex($databaseId: String!, $collectionId: String!, $key: String!) { - databasesDeleteIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + collectionsDeleteIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { status } }'; case self::$GET_ATTRIBUTES: return 'query listAttributes($databaseId: String!, $collectionId: String!) { - databasesListAttributes(databaseId: $databaseId, collectionId: $collectionId) { + collectionsListAttributes(databaseId: $databaseId, collectionId: $collectionId) { total attributes { ...attributeProperties @@ -658,19 +658,19 @@ trait Base }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; case self::$GET_ATTRIBUTE: return 'query getAttribute($databaseId: String!, $collectionId: String!, $key: String!) { - databasesGetAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + collectionsGetAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { ...attributeProperties } }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; case self::$DELETE_ATTRIBUTE: return 'mutation deleteAttribute($databaseId: String!, $collectionId: String!, $key: String!) { - databasesDeleteAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + collectionsDeleteAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { status } }'; case self::$GET_DOCUMENT: return 'query getDocument($databaseId: String!, $collectionId: String!, $documentId: String!) { - databasesGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { + collectionsGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { _id _collectionId _permissions @@ -679,7 +679,7 @@ trait Base }'; case self::$GET_DOCUMENTS: return 'query listDocuments($databaseId: String!, $collectionId: String!){ - databasesListDocuments(databaseId: $databaseId, collectionId: $collectionId) { + collectionsListDocuments(databaseId: $databaseId, collectionId: $collectionId) { total documents { _id @@ -691,7 +691,7 @@ trait Base }'; case self::$CREATE_DOCUMENT: return 'mutation createDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ - databasesCreateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + collectionsCreateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id _collectionId _permissions @@ -758,7 +758,7 @@ trait Base }'; case self::$UPDATE_DOCUMENT: return 'mutation updateDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ - databasesUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + collectionsUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id _collectionId data @@ -766,7 +766,7 @@ trait Base }'; case self::$DELETE_DOCUMENT: return 'mutation deleteDocument($databaseId: String!, $collectionId: String!, $documentId: String!){ - databasesDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { + collectionsDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { status } }'; diff --git a/tests/e2e/Services/GraphQL/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/DatabaseClientTest.php index 3853a3fc17..f3029d60b4 100644 --- a/tests/e2e/Services/GraphQL/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseClientTest.php @@ -108,7 +108,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateStringAttribute']); return $data; } @@ -140,7 +140,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateIntegerAttribute']); return $data; } @@ -181,7 +181,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesCreateDocument']; + $document = $document['body']['data']['collectionsCreateDocument']; $this->assertIsArray($document); return [ @@ -214,7 +214,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['databasesListDocuments']); + $this->assertIsArray($documents['body']['data']['collectionsListDocuments']); } /** @@ -241,7 +241,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['databasesGetDocument']); + $this->assertIsArray($document['body']['data']['collectionsGetDocument']); } /** @@ -271,7 +271,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesUpdateDocument']; + $document = $document['body']['data']['collectionsUpdateDocument']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); diff --git a/tests/e2e/Services/GraphQL/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/DatabaseServerTest.php index 87006a1bea..cdd2f4d69a 100644 --- a/tests/e2e/Services/GraphQL/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/DatabaseServerTest.php @@ -135,7 +135,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateStringAttribute']); return $data; } @@ -168,9 +168,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateStringAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateStringAttribute']['required']); - $this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateStringAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateStringAttribute']['required']); + $this->assertEquals('Default Value', $attribute['body']['data']['collectionsUpdateStringAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -203,7 +203,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateIntegerAttribute']); return $data; } @@ -238,11 +238,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateIntegerAttribute']['required']); - $this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerAttribute']['min']); - $this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerAttribute']['max']); - $this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateIntegerAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateIntegerAttribute']['required']); + $this->assertEquals(12, $attribute['body']['data']['collectionsUpdateIntegerAttribute']['min']); + $this->assertEquals(160, $attribute['body']['data']['collectionsUpdateIntegerAttribute']['max']); + $this->assertEquals(50, $attribute['body']['data']['collectionsUpdateIntegerAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -273,7 +273,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateBooleanAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateBooleanAttribute']); return $data; } @@ -306,9 +306,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateBooleanAttribute']['required']); - $this->assertTrue($attribute['body']['data']['databasesUpdateBooleanAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateBooleanAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateBooleanAttribute']['required']); + $this->assertTrue($attribute['body']['data']['collectionsUpdateBooleanAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -342,7 +342,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateFloatAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateFloatAttribute']); return $data; } @@ -377,11 +377,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateFloatAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateFloatAttribute']['required']); - $this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['min']); - $this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['max']); - $this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateFloatAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateFloatAttribute']['required']); + $this->assertEquals(100.0, $attribute['body']['data']['collectionsUpdateFloatAttribute']['min']); + $this->assertEquals(1000000.0, $attribute['body']['data']['collectionsUpdateFloatAttribute']['max']); + $this->assertEquals(2500.0, $attribute['body']['data']['collectionsUpdateFloatAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -412,7 +412,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateEmailAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateEmailAttribute']); return $data; } @@ -445,9 +445,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateEmailAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateEmailAttribute']['required']); - $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateEmailAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateEmailAttribute']['required']); + $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['collectionsUpdateEmailAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -483,7 +483,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateEnumAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateEnumAttribute']); return $data; } @@ -522,11 +522,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateEnumAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateEnumAttribute']['required']); - $this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['default']); - $this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); - $this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateEnumAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateEnumAttribute']['required']); + $this->assertEquals('tech', $attribute['body']['data']['collectionsUpdateEnumAttribute']['default']); + $this->assertContains('tech', $attribute['body']['data']['collectionsUpdateEnumAttribute']['elements']); + $this->assertNotContains('guest', $attribute['body']['data']['collectionsUpdateEnumAttribute']['elements']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -557,7 +557,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateDatetimeAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateDatetimeAttribute']); return $data; } @@ -590,9 +590,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeAttribute']['required']); - $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateDatetimeAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateDatetimeAttribute']['required']); + $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['collectionsUpdateDatetimeAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -625,7 +625,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateRelationshipAttribute']); return $data; } @@ -656,7 +656,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateRelationshipAttribute']); return $data; } @@ -687,7 +687,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateIpAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateIpAttribute']); return $data; } @@ -720,9 +720,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateIpAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateIpAttribute']['required']); - $this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateIpAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateIpAttribute']['required']); + $this->assertEquals('127.0.0.1', $attribute['body']['data']['collectionsUpdateIpAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -754,7 +754,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesCreateUrlAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsCreateUrlAttribute']); return $data; } @@ -787,9 +787,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesUpdateUrlAttribute']); - $this->assertFalse($attribute['body']['data']['databasesUpdateUrlAttribute']['required']); - $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['collectionsUpdateUrlAttribute']); + $this->assertFalse($attribute['body']['data']['collectionsUpdateUrlAttribute']['required']); + $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['collectionsUpdateUrlAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); } @@ -823,12 +823,12 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $index['body']); $this->assertIsArray($index['body']['data']); - $this->assertIsArray($index['body']['data']['databasesCreateIndex']); + $this->assertIsArray($index['body']['data']['collectionsCreateIndex']); return [ 'database' => $data['database'], 'collection' => $data['collection'], - 'index' => $index['body']['data']['databasesCreateIndex'], + 'index' => $index['body']['data']['collectionsCreateIndex'], ]; } @@ -874,7 +874,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesCreateDocument']; + $document = $document['body']['data']['collectionsCreateDocument']; $this->assertIsArray($document); return [ @@ -1042,7 +1042,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attributes['body']); $this->assertIsArray($attributes['body']['data']); - $this->assertIsArray($attributes['body']['data']['databasesListAttributes']); + $this->assertIsArray($attributes['body']['data']['collectionsListAttributes']); } /** @@ -1069,7 +1069,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['databasesGetAttribute']); + $this->assertIsArray($attribute['body']['data']['collectionsGetAttribute']); } /** @@ -1095,7 +1095,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $indices['body']); $this->assertIsArray($indices['body']['data']); - $this->assertIsArray($indices['body']['data']['databasesListIndexes']); + $this->assertIsArray($indices['body']['data']['collectionsListIndexes']); } /** @@ -1122,7 +1122,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $index['body']); $this->assertIsArray($index['body']['data']); - $this->assertIsArray($index['body']['data']['databasesGetIndex']); + $this->assertIsArray($index['body']['data']['collectionsGetIndex']); } /** @@ -1148,7 +1148,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['databasesListDocuments']); + $this->assertIsArray($documents['body']['data']['collectionsListDocuments']); } /** @@ -1175,7 +1175,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['databasesGetDocument']); + $this->assertIsArray($document['body']['data']['collectionsGetDocument']); } // /** @@ -1306,7 +1306,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['databasesUpdateDocument']; + $document = $document['body']['data']['collectionsUpdateDocument']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); } From a067b77e90d737dfb2cc9644052e18b6cdfb5b55 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 9 May 2025 20:04:02 +0530 Subject: [PATCH 093/173] update: model; add|update: graphql tests [wip]. --- .../Http/Databases/Tables/Create.php | 2 +- .../Http/Databases/Tables/Update.php | 4 +- src/Appwrite/Utopia/Response/Model/Table.php | 8 +- .../Databases/Tables/DatabasesBase.php | 46 +- .../Tables/DatabasesConsoleClientTest.php | 4 +- .../Tables/DatabasesCustomClientTest.php | 28 +- .../Tables/DatabasesCustomServerTest.php | 24 +- .../Tables/DatabasesPermissionsGuestTest.php | 4 +- .../Tables/DatabasesPermissionsMemberTest.php | 6 +- tests/e2e/Services/GraphQL/Base.php | 450 ++++- .../GraphQL/{ => Collections}/AbuseTest.php | 5 +- .../GraphQL/{ => Collections}/AuthTest.php | 3 +- .../{ => Collections}/DatabaseClientTest.php | 3 +- .../{ => Collections}/DatabaseServerTest.php | 3 +- .../e2e/Services/GraphQL/Tables/AbuseTest.php | 185 +++ .../e2e/Services/GraphQL/Tables/AuthTest.php | 253 +++ .../GraphQL/Tables/DatabaseClientTest.php | 306 ++++ .../GraphQL/Tables/DatabaseServerTest.php | 1467 +++++++++++++++++ 18 files changed, 2732 insertions(+), 69 deletions(-) rename tests/e2e/Services/GraphQL/{ => Collections}/AbuseTest.php (97%) rename tests/e2e/Services/GraphQL/{ => Collections}/AuthTest.php (99%) rename tests/e2e/Services/GraphQL/{ => Collections}/DatabaseClientTest.php (99%) rename tests/e2e/Services/GraphQL/{ => Collections}/DatabaseServerTest.php (99%) create mode 100644 tests/e2e/Services/GraphQL/Tables/AbuseTest.php create mode 100644 tests/e2e/Services/GraphQL/Tables/AuthTest.php create mode 100644 tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php create mode 100644 tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 0ca46f66e0..fcd52d4854 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -62,7 +62,7 @@ class Create extends CollectionCreate ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index 85024e4008..70ac9d7b7c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -61,8 +61,8 @@ class Update extends CollectionUpdate ->param('tableId', '', new UID(), 'Table ID.') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index 1b890d9e60..722edcd4cf 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -54,9 +54,9 @@ class Table extends Model 'default' => true, 'example' => false, ]) - ->addRule('documentSecurity', [ + ->addRule('rowSecurity', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Whether document-level permissions are enabled. [Learn more about permissions](https://appwrite.io/docs/permissions).', + 'description' => 'Whether row-level permissions are enabled. [Learn more about permissions](https://appwrite.io/docs/permissions).', 'default' => '', 'example' => true, ]) @@ -125,9 +125,13 @@ class Table extends Model $document->setAttribute('relatedTable', $related); } + $documentSecurity = $document->getAttribute('documentSecurity'); + $document->setAttribute('rowSecurity', $documentSecurity); + // remove anyways as they are already copied above. $document ->removeAttribute('attributes') + ->removeAttribute('documentSecurity') ->removeAttribute('relatedCollection'); return $document; diff --git a/tests/e2e/Services/Databases/Tables/DatabasesBase.php b/tests/e2e/Services/Databases/Tables/DatabasesBase.php index 5532bbac46..ef35d3c962 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesBase.php @@ -52,7 +52,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Movies', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), ], @@ -68,7 +68,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Actors', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), ], @@ -136,7 +136,7 @@ trait DatabasesBase ]), [ 'name' => 'Movies', 'enabled' => false, - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -182,7 +182,7 @@ trait DatabasesBase ]), [ 'name' => 'Movies', 'enabled' => true, - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -418,7 +418,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'patch', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), ], @@ -489,7 +489,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Players', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), ], @@ -542,7 +542,7 @@ trait DatabasesBase 'tableId' => ID::unique(), 'name' => 'Response Models', // 'permissions' missing on purpose to make sure it's optional - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $table['headers']['status-code']); @@ -2479,7 +2479,7 @@ trait DatabasesBase Permission::create(Role::any()), Permission::read(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $table['headers']['status-code']); @@ -3146,7 +3146,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'enforceCollectionAndDocumentPermissions', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::read(Role::user($user)), Permission::create(Role::user($user)), @@ -3157,7 +3157,7 @@ trait DatabasesBase $this->assertEquals(201, $table['headers']['status-code']); $this->assertEquals($table['body']['name'], 'enforceCollectionAndDocumentPermissions'); - $this->assertEquals($table['body']['documentSecurity'], true); + $this->assertEquals($table['body']['rowSecurity'], true); $tableId = $table['body']['$id']; @@ -3349,7 +3349,7 @@ trait DatabasesBase $this->assertEquals(201, $table['headers']['status-code']); $this->assertEquals($table['body']['name'], 'enforceCollectionPermissions'); - $this->assertEquals($table['body']['documentSecurity'], false); + $this->assertEquals($table['body']['rowSecurity'], false); $tableId = $table['body']['$id']; @@ -3500,7 +3500,7 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ], [ 'name' => $table['body']['name'], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $rowsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ @@ -3698,7 +3698,7 @@ trait DatabasesBase Permission::update(Role::user(ID::custom($this->getUser()['$id']))), Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $movies['headers']['status-code']); @@ -3854,7 +3854,7 @@ trait DatabasesBase Permission::delete(Role::user($this->getUser()['$id'])), Permission::create(Role::user($this->getUser()['$id'])), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $person['headers']['status-code']); @@ -3871,7 +3871,7 @@ trait DatabasesBase Permission::update(Role::user($this->getUser()['$id'])), Permission::create(Role::user($this->getUser()['$id'])), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $library['headers']['status-code']); @@ -4238,7 +4238,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Albums', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -4264,7 +4264,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Artists', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -4382,7 +4382,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Sports', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -4408,7 +4408,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Players', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -4642,7 +4642,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'USA Presidents', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), ], @@ -4765,7 +4765,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Collection1', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -4779,7 +4779,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Collection2', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -4861,7 +4861,7 @@ trait DatabasesBase ]), [ 'tableId' => ID::unique(), 'name' => 'Slow Queries', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), ], diff --git a/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php index 48707d444b..087ece167f 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php @@ -46,7 +46,7 @@ class DatabasesConsoleClientTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $movies['headers']['status-code']); @@ -77,7 +77,7 @@ class DatabasesConsoleClientTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); /** diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php index ca8ae08ca0..6893588934 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php @@ -42,7 +42,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'Movies', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::write(Role::user($this->getUser()['$id'])), ], @@ -141,7 +141,7 @@ class DatabasesCustomClientTest extends Scope 'tableId' => ID::custom('permissionCheck'), 'name' => 'permissionCheck', 'permissions' => [], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -249,7 +249,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'level1', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -266,7 +266,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'level2', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -337,7 +337,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'c1', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -353,7 +353,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'c2', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -497,7 +497,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection1'), 'name' => ID::custom('collection1'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($userId)), Permission::read(Role::user($userId)), @@ -513,7 +513,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection2'), 'name' => ID::custom('collection2'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::read(Role::user($userId)), ] @@ -526,7 +526,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($userId)), Permission::read(Role::user($userId)), @@ -541,7 +541,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection4'), 'name' => ID::custom('collection4'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::read(Role::user($userId)), ] @@ -554,7 +554,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection5'), 'name' => ID::custom('collection5'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($userId)), Permission::read(Role::user($userId)), @@ -741,7 +741,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($userId)), Permission::read(Role::user($userId)), @@ -816,7 +816,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection3'), 'name' => ID::custom('collection3'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($userId)), Permission::read(Role::user($userId)), @@ -832,7 +832,7 @@ class DatabasesCustomClientTest extends Scope ]), [ 'tableId' => ID::custom('collection2'), 'name' => ID::custom('collection2'), - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($userId)), Permission::update(Role::user($userId)), diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php index dca8a16363..a92f1decf1 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php @@ -365,7 +365,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $test2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ @@ -381,7 +381,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $tables = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables', array_merge([ @@ -586,7 +586,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(409, $response['headers']['status-code']); @@ -665,7 +665,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $actors['headers']['status-code']); @@ -775,7 +775,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $actors['headers']['status-code']); @@ -1058,7 +1058,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $table['headers']['status-code']); @@ -1251,7 +1251,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'Collection1', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [], ]); @@ -1262,7 +1262,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'tableId' => ID::unique(), 'name' => 'Collection2', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [], ]); @@ -1326,7 +1326,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $table['headers']['status-code']); @@ -1392,7 +1392,7 @@ class DatabasesCustomServerTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $table['headers']['status-code']); @@ -3632,7 +3632,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'tableId' => 'collection1', 'name' => 'level1', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), @@ -3648,7 +3648,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'tableId' => 'collection2', 'name' => 'level2', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php index fa04ba04aa..fde6041219 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php @@ -45,7 +45,7 @@ class DatabasesPermissionsGuestTest extends Scope 'tableId' => ID::unique(), 'name' => 'Movies', 'permissions' => [], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $publicCollection = ['id' => $publicMovies['body']['$id']]; @@ -246,7 +246,7 @@ class DatabasesPermissionsGuestTest extends Scope 'permissions' => [ Permission::create(Role::any()), ], - 'documentSecurity' => true + 'rowSecurity' => true ]); $moviesId = $movies['body']['$id']; diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php index 8da1d9e1ed..7b0e9d8a64 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php @@ -133,7 +133,7 @@ class DatabasesPermissionsMemberTest extends Scope Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $public['headers']['status-code']); $this->collections = ['public' => $public['body']['$id']]; @@ -154,7 +154,7 @@ class DatabasesPermissionsMemberTest extends Scope Permission::update(Role::users()), Permission::delete(Role::users()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $private['headers']['status-code']); $this->collections['private'] = $private['body']['$id']; @@ -170,7 +170,7 @@ class DatabasesPermissionsMemberTest extends Scope 'tableId' => ID::unique(), 'name' => 'Document Only Movies', 'permissions' => [], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $private['headers']['status-code']); $this->collections['doconly'] = $doconly['body']['$id']; diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index f7ee14f7e1..65154e2f93 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -13,12 +13,21 @@ trait Base public static string $GET_DATABASE = 'get_database'; public static string $UPDATE_DATABASE = 'update_database'; public static string $DELETE_DATABASE = 'delete_database'; + // Collections public static string $CREATE_COLLECTION = 'create_collection'; public static string $GET_COLLECTION = 'get_collection'; public static string $GET_COLLECTIONS = 'list_collections'; public static string $UPDATE_COLLECTION = 'update_collection'; public static string $DELETE_COLLECTION = 'delete_collection'; + + // Tables + public static string $CREATE_TABLE = 'create_table'; + public static string $GET_TABLE = 'get_table'; + public static string $GET_TABLES = 'list_tables'; + public static string $UPDATE_TABLE = 'update_tables'; + public static string $DELETE_TABLE = 'delete_tables'; + // Attributes public static string $CREATE_STRING_ATTRIBUTE = 'create_string_attribute'; public static string $CREATE_INTEGER_ATTRIBUTE = 'create_integer_attribute'; @@ -45,11 +54,46 @@ trait Base public static string $GET_ATTRIBUTES = 'get_attributes'; public static string $GET_ATTRIBUTE = 'get_attribute'; public static string $DELETE_ATTRIBUTE = 'delete_attribute'; - // Indexes + + // Columns + public static string $CREATE_STRING_COLUMN = 'create_string_column'; + public static string $CREATE_INTEGER_COLUMN = 'create_integer_column'; + public static string $CREATE_FLOAT_COLUMN = 'create_float_column'; + public static string $CREATE_BOOLEAN_COLUMN = 'create_boolean_column'; + public static string $CREATE_URL_COLUMN = 'create_url_column'; + public static string $CREATE_EMAIL_COLUMN = 'create_email_column'; + public static string $CREATE_IP_COLUMN = 'create_ip_column'; + public static string $CREATE_ENUM_COLUMN = 'create_enum_column'; + public static string $CREATE_DATETIME_COLUMN = 'create_datetime_column'; + + public static string $CREATE_RELATIONSHIP_COLUMN = 'create_relationship_column'; + public static string $UPDATE_STRING_COLUMN = 'update_string_column'; + public static string $UPDATE_INTEGER_COLUMN = 'update_integer_column'; + public static string $UPDATE_FLOAT_COLUMN = 'update_float_column'; + public static string $UPDATE_BOOLEAN_COLUMN = 'update_boolean_column'; + public static string $UPDATE_URL_COLUMN = 'update_url_column'; + public static string $UPDATE_EMAIL_COLUMN = 'update_email_column'; + public static string $UPDATE_IP_COLUMN = 'update_ip_column'; + public static string $UPDATE_ENUM_COLUMN = 'update_enum_column'; + public static string $UPDATE_DATETIME_COLUMN = 'update_datetime_column'; + + public static string $UPDATE_RELATIONSHIP_COLUMN = 'update_relationship_column'; + public static string $GET_COLUMNS = 'get_columns'; + public static string $GET_COLUMN = 'get_column'; + public static string $DELETE_COLUMN = 'delete_column'; + + // Collection Indexes public static string $CREATE_INDEX = 'create_index'; public static string $GET_INDEXES = 'get_indexes'; public static string $GET_INDEX = 'get_index'; public static string $DELETE_INDEX = 'delete_index'; + + // Column Indexes + public static string $CREATE_COLUMN_INDEX = 'create_index'; + public static string $GET_COLUMN_INDEXES = 'get_indexes'; + public static string $GET_COLUMN_INDEX = 'get_index'; + public static string $DELETE_COLUMN_INDEX = 'delete_index'; + // Documents public static string $CREATE_DOCUMENT = 'create_document_rest'; public static string $GET_DOCUMENTS = 'list_documents'; @@ -57,6 +101,13 @@ trait Base public static string $UPDATE_DOCUMENT = 'update_document'; public static string $DELETE_DOCUMENT = 'delete_document'; + // Rows + public static string $CREATE_ROW = 'create_row_rest'; + public static string $GET_ROWS = 'list_rows'; + public static string $GET_ROW = 'get_row'; + public static string $UPDATE_ROW = 'update_row'; + public static string $DELETE_ROW = 'delete_row'; + // Custom Entities public static string $CREATE_CUSTOM_ENTITY = 'create_custom_entity'; public static string $GET_CUSTOM_ENTITIES = 'get_custom_entities'; @@ -257,7 +308,7 @@ trait Base // Complex queries public static string $COMPLEX_QUERY = 'complex_query'; - // Fragments + // Attribute Fragments public static string $FRAGMENT_ATTRIBUTES = ' fragment attributeProperties on Attributes { ... on AttributeString { @@ -332,6 +383,81 @@ trait Base } '; + // Column Fragments + public static string $FRAGMENT_COLUMNS = ' + fragment columnProperties on Columns { + ... on ColumnString { + key + required + array + status + default + size + } + ... on ColumnInteger { + key + required + array + status + intDefault: default + intMin: min + intMax: max + } + ... on ColumnFloat { + key + required + array + status + floatDefault: default + floatMin: min + floatMax: max + } + ... on ColumnBoolean { + key + required + array + status + boolDefault: default + } + ... on ColumnUrl { + key + required + array + status + default + } + ... on ColumnEmail { + key + required + array + status + default + } + ... on ColumnIp { + key + required + array + status + default + } + ... on ColumnEnum { + key + required + array + status + default + elements + } + ... on ColumnDatetime { + key + required + array + status + default + } + } + '; + public static string $FRAGMENT_HASH_OPTIONS = ' fragment options on HashOptions { ... on AlgoArgon2 { @@ -438,6 +564,51 @@ trait Base status } }'; + case self::$GET_TABLE: + return 'query getTable($databaseId: String!, $tableId: String!) { + databasesGetTable(databaseId: $databaseId, tableId: $tableId) { + _id + _permissions + rowSecurity + name + } + }'; + case self::$GET_TABLES: + return 'query listTables($databaseId: String!) { + databasesListTables(databaseId: $databaseId) { + total + tables { + _id + _permissions + rowSecurity + name + } + } + }'; + case self::$CREATE_TABLE: + return 'mutation createTable($databaseId: String!, $tableId: String!, $name: String!, $rowSecurity: Boolean!, $permissions: [String!]!) { + databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $name, rowSecurity: $rowSecurity, permissions: $permissions) { + _id + _permissions + rowSecurity + name + } + }'; + case self::$UPDATE_TABLE: + return 'mutation updateTable($databaseId: String!, $tableId: String!, $name: String!, $rowSecurity: Boolean!, $permissions: [String!], $enabled: Boolean) { + databasesUpdateTable(databaseId: $databaseId, tableId: $tableId, name: $name, rowSecurity: $rowSecurity, permissions: $permissions, enabled: $enabled) { + _id + _permissions + rowSecurity + name + } + }'; + case self::$DELETE_TABLE: + return 'mutation deleteTable($databaseId: String!, $tableId: String!) { + databasesDeleteTable(databaseId: $databaseId, tableId: $tableId) { + status + } + }'; case self::$CREATE_STRING_ATTRIBUTE: return 'mutation createStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ collectionsCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { @@ -614,6 +785,182 @@ trait Base onDelete } }'; + case self::$CREATE_STRING_COLUMN: + return 'mutation createStringColumn($databaseId: String!, $tableId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ + tablesCreateStringColumn(databaseId: $databaseId, tableId: $tableId, key: $key, size: $size, required: $required, default: $default, array: $array) { + key + required + default + array + } + }'; + case self::$CREATE_INTEGER_COLUMN: + return 'mutation createIntegerColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ + tablesCreateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + key + required + min + max + default + array + } + }'; + case self::$CREATE_FLOAT_COLUMN: + return 'mutation createFloatColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ + tablesCreateFloatColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + key + required + min + max + default + array + } + }'; + case self::$CREATE_BOOLEAN_COLUMN: + return 'mutation createBooleanColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ + tablesCreateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + key + required + default + array + } + }'; + case self::$CREATE_URL_COLUMN: + return 'mutation createUrlColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + tablesCreateUrlColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + key + required + default + array + } + }'; + case self::$CREATE_EMAIL_COLUMN: + return 'mutation createEmailColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + tablesCreateEmailColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + key + required + default + array + } + }'; + case self::$CREATE_IP_COLUMN: + return 'mutation createIpColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + tablesCreateIpColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + key + required + default + array + } + }'; + case self::$CREATE_ENUM_COLUMN: + return 'mutation createEnumColumn($databaseId: String!, $tableId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ + tablesCreateEnumColumn(databaseId: $databaseId, tableId: $tableId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { + key + elements + required + default + array + } + }'; + case self::$CREATE_DATETIME_COLUMN: + return 'mutation createDatetimeColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ + tablesCreateDatetimeColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default, array: $array) { + key + required + default + array + } + }'; + case self::$CREATE_RELATIONSHIP_COLUMN: + return 'mutation createRelationshipColumn($databaseId: String!, $tableId: String!, $relatedTableId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ + tablesCreateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, relatedTableId: $relatedTableId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { + relatedTable + relationType + twoWay + key + twoWayKey + onDelete + } + }'; + case self::$UPDATE_STRING_COLUMN: + return 'mutation updateStringColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + tablesUpdateStringColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + required + default + } + }'; + case self::$UPDATE_INTEGER_COLUMN: + return 'mutation updateIntegerColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ + tablesUpdateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, min: $min, max: $max, default: $default) { + required + min + max + default + } + }'; + case self::$UPDATE_FLOAT_COLUMN: + return 'mutation updateFloatColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ + tablesUpdateFloatColumn(databaseId: $databaseId, tableId: $tableId, key: $key, min: $min, max: $max, required: $required, default: $default) { + required + min + max + default + } + }'; + case self::$UPDATE_BOOLEAN_COLUMN: + return 'mutation updateBooleanColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: Boolean){ + tablesUpdateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + required + default + } + }'; + case self::$UPDATE_URL_COLUMN: + return 'mutation updateUrlColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + tablesUpdateUrlColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + required + default + } + }'; + case self::$UPDATE_EMAIL_COLUMN: + return 'mutation updateEmailColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + tablesUpdateEmailColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + required + default + } + }'; + case self::$UPDATE_IP_COLUMN: + return 'mutation updateIpColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + tablesUpdateIpColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + required + default + } + }'; + case self::$UPDATE_ENUM_COLUMN: + return 'mutation updateEnumColumn($databaseId: String!, $tableId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ + tablesUpdateEnumColumn(databaseId: $databaseId, tableId: $tableId, key: $key, elements: $elements, required: $required, default: $default) { + elements + required + default + } + }'; + case self::$UPDATE_DATETIME_COLUMN: + return 'mutation updateDatetimeColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $default: String){ + tablesUpdateDatetimeColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, default: $default) { + required + default + } + }'; + case self::$UPDATE_RELATIONSHIP_COLUMN: + return 'mutation updateRelationshipColumn($databaseId: String!, $tableId: String!, $key: String!, $onDelete: String){ + tablesUpdateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, key: $key, onDelete: $onDelete) { + relatedTable + relationType + twoWay + key + twoWayKey + onDelete + } + }'; case self::$CREATE_INDEX: return 'mutation createIndex($databaseId: String!, $collectionId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ collectionsCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { @@ -647,6 +994,39 @@ trait Base status } }'; + case self::$CREATE_COLUMN_INDEX: + return 'mutation createIndex($databaseId: String!, $tableId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ + tablesCreateIndex(databaseId: $databaseId, tableId: $tableId, key: $key, type: $type, attributes: $attributes, orders: $orders) { + key + type + status + } + }'; + case self::$GET_COLUMN_INDEXES: + return 'query listIndexes($databaseId: String!, $tableId: String!) { + tablesListIndexes(databaseId: $databaseId, tableId: $tableId) { + total + indexes { + key + type + status + } + } + }'; + case self::$GET_COLUMN_INDEX: + return 'query getIndex($databaseId: String!, $tableId: String!, $key: String!) { + tablesGetIndex(databaseId: $databaseId, tableId: $tableId, key: $key) { + key + type + status + } + }'; + case self::$DELETE_COLUMN_INDEX: + return 'mutation deleteIndex($databaseId: String!, $tableId: String!, $key: String!) { + tablesDeleteIndex(databaseId: $databaseId, tableId: $tableId, key: $key) { + status + } + }'; case self::$GET_ATTRIBUTES: return 'query listAttributes($databaseId: String!, $collectionId: String!) { collectionsListAttributes(databaseId: $databaseId, collectionId: $collectionId) { @@ -668,6 +1048,28 @@ trait Base status } }'; + case self::$GET_COLUMNS: + return 'query listColumns($databaseId: String!, $tableId: String!) { + tablesListColumns(databaseId: $databaseId, tableId: $tableId) { + total + columns { + ...columnProperties + } + } + }' . PHP_EOL . self::$FRAGMENT_COLUMNS; + case self::$GET_COLUMN: + return 'query getColumn($databaseId: String!, $tableId: String!, $key: String!) { + tablesGetColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { + ...columnProperties + } + }' . PHP_EOL . self::$FRAGMENT_COLUMNS; + case self::$DELETE_COLUMN: + return 'mutation deleteColumn($databaseId: String!, $tableId: String!, $key: String!) { + tablesDeleteColumn(databaseId: $databaseId, tableId: $tableId, key: $key) { + status + } + }'; + case self::$GET_DOCUMENT: return 'query getDocument($databaseId: String!, $collectionId: String!, $documentId: String!) { collectionsGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { @@ -697,6 +1099,35 @@ trait Base _permissions } }'; + case self::$GET_ROW: + return 'query getRow($databaseId: String!, $tableId: String!, $rowId: String!) { + tablesGetRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { + _id + _tableId + _permissions + data + } + }'; + case self::$GET_ROWS: + return 'query listRows($databaseId: String!, $tableId: String!) { + tablesListRows(databaseId: $databaseId, tableId: $tableId) { + total + rows { + _id + _tableId + _permissions + data + } + } + }'; + case self::$CREATE_ROW: + return 'mutation createRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]) { + tablesCreateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { + _id + _tableId + _permissions + } + }'; case self::$CREATE_CUSTOM_ENTITY: return 'mutation createActor($name: String!, $age: Int!, $alive: Boolean!, $salary: Float, $email: String!, $role: String!, $dob: String!, $ip: String, $url: String){ actorsCreate(name: $name, age: $age, alive: $alive, salary: $salary, email: $email, role: $role, dob: $dob, ip: $ip, url: $url) { @@ -770,7 +1201,20 @@ trait Base status } }'; - + case self::$UPDATE_ROW: + return 'mutation updateRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!]) { + tablesUpdateRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { + _id + _tableId + data + } + }'; + case self::$DELETE_ROW: + return 'mutation deleteRow($databaseId: String!, $tableId: String!, $rowId: String!) { + tablesDeleteRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { + status + } + }'; case self::$GET_USER: return 'query getUser($userId : String!) { usersGet(userId : $userId) { diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/Collections/AbuseTest.php similarity index 97% rename from tests/e2e/Services/GraphQL/AbuseTest.php rename to tests/e2e/Services/GraphQL/Collections/AbuseTest.php index ea97492c2b..a45bebd714 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Collections/AbuseTest.php @@ -1,11 +1,12 @@ assertEquals('Too many queries.', $response['body']['message']); } - private function createCollection() + private function createCollection(): array { $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::$CREATE_DATABASE); diff --git a/tests/e2e/Services/GraphQL/AuthTest.php b/tests/e2e/Services/GraphQL/Collections/AuthTest.php similarity index 99% rename from tests/e2e/Services/GraphQL/AuthTest.php rename to tests/e2e/Services/GraphQL/Collections/AuthTest.php index db56537999..288987a719 100644 --- a/tests/e2e/Services/GraphQL/AuthTest.php +++ b/tests/e2e/Services/GraphQL/Collections/AuthTest.php @@ -1,11 +1,12 @@ markTestSkipped('Abuse is not enabled.'); + } + } + + public function testRateLimitEnforced() + { + $data = $this->createCollection(); + $databaseId = $data['databaseId']; + $tableId = $data['tableId']; + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_ROW); + $max = 120; + + for ($i = 0; $i <= $max + 1; $i++) { + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => ID::unique(), + 'data' => [ + 'name' => 'John Doe', + ], + ], + ]; + + $response = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $gqlPayload); + + if ($i < $max) { + $this->assertArrayNotHasKey('errors', $response['body']); + } else { + $this->assertArrayHasKey('errors', $response['body']); + } + } + } + + public function testComplexQueryBlocked() + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$COMPLEX_QUERY); + $graphQLPayload = [ + 'query' => $query, + 'variables' => [ + 'userId' => 'user', + 'email' => 'user@appwrite.io', + 'password' => 'password', + 'databaseId' => 'database', + 'databaseName' => 'database', + 'tableId' => 'table', + 'collectionName' => 'table', + 'collectionPermissions' => [ + Permission::read(Role::users()), + Permission::create(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + 'rowSecurity' => false, + ], + ]; + + $response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $graphQLPayload); + + $max = System::getEnv('_APP_GRAPHQL_MAX_QUERY_COMPLEXITY', 250); + + $this->assertEquals('Max query complexity should be ' . $max . ' but got 259.', $response['body']['errors'][0]['message']); + } + + public function testTooManyQueriesBlocked() + { + $projectId = $this->getProject()['$id']; + $maxQueries = System::getEnv('_APP_GRAPHQL_MAX_QUERIES', 10); + + $query = []; + for ($i = 0; $i <= $maxQueries + 1; $i++) { + $query[] = ['query' => $this->getQuery(self::$LIST_COUNTRIES)]; + } + + $response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $query); + + $this->assertEquals('Too many queries.', $response['body']['message']); + } + + private function createCollection(): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => 'actors', + 'name' => 'AbuseDatabase', + ] + ]; + + $response = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + $databaseId = $response['body']['data']['databasesCreate']['_id']; + + $query = $this->getQuery(self::$CREATE_TABLE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $databaseId, + 'tableId' => 'actors', + 'name' => 'Actors', + 'rowSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::write(Role::any()), + ], + ] + ]; + + $response = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + $tableId = $response['body']['data']['databasesCreateTable']['_id']; + + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'key' => 'name', + 'size' => 256, + 'required' => true, + ] + ]; + + $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + sleep(2); + + return [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + ]; + } +} diff --git a/tests/e2e/Services/GraphQL/Tables/AuthTest.php b/tests/e2e/Services/GraphQL/Tables/AuthTest.php new file mode 100644 index 0000000000..681607d599 --- /dev/null +++ b/tests/e2e/Services/GraphQL/Tables/AuthTest.php @@ -0,0 +1,253 @@ +getProject()['$id']; + $query = $this->getQuery(self::$CREATE_ACCOUNT); + + $email1 = 'test' . \rand() . '@test.com'; + $email2 = 'test' . \rand() . '@test.com'; + + // Create account 1 + $graphQLPayload = [ + 'query' => $query, + 'variables' => [ + 'userId' => ID::unique(), + 'name' => 'User Name', + 'email' => $email1, + 'password' => 'password', + ], + ]; + $this->account1 = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $graphQLPayload); + + // Create account 2 + $graphQLPayload['variables']['userId'] = ID::unique(); + $graphQLPayload['variables']['email'] = $email2; + + $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $graphQLPayload); + + // Create session 1 + $query = $this->getQuery(self::$CREATE_ACCOUNT_SESSION); + $graphQLPayload = [ + 'query' => $query, + 'variables' => [ + 'email' => $email1, + 'password' => 'password', + ] + ]; + $session1 = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $graphQLPayload); + + $this->token1 = $session1['cookies']['a_session_' . $projectId]; + + // Create session 2 + $graphQLPayload['variables']['email'] = $email2; + + $session2 = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $graphQLPayload); + + $this->token2 = $session2['cookies']['a_session_' . $projectId]; + + // Create database + $query = $this->getQuery(self::$CREATE_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => ID::unique(), + 'name' => 'Actors', + ] + ]; + $this->database = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + // Create table + $query = $this->getQuery(self::$CREATE_TABLE); + $userId = $this->account1['body']['data']['accountCreate']['_id']; + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], + 'tableId' => ID::unique(), + 'name' => 'Actors', + 'rowSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($userId)) + ] + ] + ]; + $this->table = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + // Create string attribute + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'key' => 'name', + 'size' => 256, + 'required' => true, + ] + ]; + $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + sleep(1); + } + + public function testInvalidAuth() + { + $projectId = $this->getProject()['$id']; + + // Create row as account 1 + $query = $this->getQuery(self::$CREATE_ROW); + $userId = $this->account1['body']['data']['accountCreate']['_id']; + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => ID::unique(), + 'data' => [ + 'name' => 'John Doe', + ], + 'permissions' => [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ] + ] + ]; + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, + ], $gqlPayload); + + // Try to read as account 1 + $query = $this->getQuery(self::$GET_ROW); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => $row['body']['data']['tablesCreateRow']['_id'], + ] + ]; + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, + ], $gqlPayload); + + $this->assertIsArray($row['body']['data']['tablesGetRow']); + $this->assertArrayNotHasKey('errors', $row['body']); + + // Try to read as account 2 + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'cookie' => 'a_session_' . $projectId . '=' . $this->token2, + ], $gqlPayload); + + $this->assertArrayHasKey('errors', $row['body']); + $this->assertEquals('Row with the requested ID could not be found.', $row['body']['errors'][0]['message']); + } + + public function testValidAuth() + { + $projectId = $this->getProject()['$id']; + + // Create row as account 1 + $query = $this->getQuery(self::$CREATE_ROW); + $userId = $this->account1['body']['data']['accountCreate']['_id']; + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => ID::unique(), + 'data' => [ + 'name' => 'John Doe', + ], + 'permissions' => [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ], + ] + ]; + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, + ], $gqlPayload); + + // Try to delete as account 1 + $query = $this->getQuery(self::$DELETE_ROW); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], + 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'rowId' => $row['body']['data']['tablesCreateRow']['_id'], + ] + ]; + $row = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, + ], $gqlPayload); + + $this->assertIsNotArray($row['body']); + $this->assertEquals(204, $row['headers']['status-code']); + } +} diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php new file mode 100644 index 0000000000..3296f2d622 --- /dev/null +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php @@ -0,0 +1,306 @@ +getProject()['$id']; + $query = $this->getQuery(self::$CREATE_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => ID::unique(), + 'name' => 'Actors', + ] + ]; + + $database = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + $this->assertIsArray($database['body']['data']); + $this->assertArrayNotHasKey('errors', $database['body']); + $database = $database['body']['data']['databasesCreate']; + $this->assertEquals('Actors', $database['name']); + + return $database; + } + + /** + * @depends testCreateDatabase + */ + public function testCreateTable($database): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_TABLE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $database['_id'], + 'tableId' => 'actors', + 'name' => 'Actors', + 'rowSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + ] + ]; + + $table = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + $this->assertIsArray($table['body']['data']); + $this->assertArrayNotHasKey('errors', $table['body']); + $table = $table['body']['data']['databasesCreateTable']; + $this->assertEquals('Actors', $table['name']); + + return [ + 'table' => $table, + 'database' => $database, + ]; + } + + /** + * @depends testCreateTable + */ + public function testCreateStringColumn($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'name', + 'size' => 256, + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['tablesCreateStringColumn']); + + return $data; + } + + /** + * @depends testCreateTable + */ + public function testCreateIntegerColumn($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_INTEGER_COLUMN); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'age', + 'min' => 18, + 'max' => 150, + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['tablesCreateIntegerColumn']); + + return $data; + } + + /** + * @depends testCreateStringColumn + * @depends testCreateIntegerColumn + */ + public function testCreateRow($data): array + { + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_ROW); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => ID::unique(), + 'data' => [ + 'name' => 'John Doe', + 'age' => 35, + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); + + $row = $row['body']['data']['tablesCreateRow']; + $this->assertIsArray($row); + + return [ + 'database' => $data['database'], + 'table' => $data['table'], + 'row' => $row, + ]; + } + + /** + * @depends testCreateTable + * @throws \Exception + */ + public function testGetRows($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_ROWS); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + ] + ]; + + $rows = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $rows['body']); + $this->assertIsArray($rows['body']['data']); + $this->assertIsArray($rows['body']['data']['tablesListRows']); + } + + /** + * @depends testCreateRow + * @throws \Exception + */ + public function testGetDocument($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_ROW); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); + $this->assertIsArray($row['body']['data']['tablesGetRow']); + } + + /** + * @depends testCreateRow + * @throws \Exception + */ + public function testUpdateRow($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_ROW); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], + 'data' => [ + 'name' => 'New Row Name', + ], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); + $row = $row['body']['data']['tablesUpdateRow']; + $this->assertIsArray($row); + + $this->assertStringContainsString('New Row Name', $row['data']); + } + + /** + * @depends testCreateRow + * @throws \Exception + */ + public function testDeleteRow($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$DELETE_ROW); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsNotArray($row['body']); + $this->assertEquals(204, $row['headers']['status-code']); + } +} diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php new file mode 100644 index 0000000000..d5980a4096 --- /dev/null +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php @@ -0,0 +1,1467 @@ +getProject()['$id']; + $query = $this->getQuery(self::$CREATE_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => 'actors', + 'name' => 'Actors', + ] + ]; + + $database = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($database['body']['data']); + $this->assertArrayNotHasKey('errors', $database['body']); + $database = $database['body']['data']['databasesCreate']; + $this->assertEquals('Actors', $database['name']); + + return $database; + } + + /** + * @depends testCreateDatabase + */ + public function testCreateCollection($database): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_COLLECTION); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $database['_id'], + 'tableId' => 'actors', + 'name' => 'Actors', + 'rowSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + ] + ]; + + $table = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($table['body']['data']); + $this->assertArrayNotHasKey('errors', $table['body']); + $table = $table['body']['data']['databasesCreateCollection']; + $this->assertEquals('Actors', $table['name']); + + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $database['_id'], + 'tableId' => 'movies', + 'name' => 'Movies', + 'rowSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + ] + ]; + + $table2 = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($table2['body']['data']); + $this->assertArrayNotHasKey('errors', $table2['body']); + $table2 = $table2['body']['data']['databasesCreateCollection']; + $this->assertEquals('Movies', $table2['name']); + + return [ + 'database' => $database, + 'table' => $table, + 'collection2' => $table2, + ]; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateStringAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'name', + 'size' => 256, + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateStringAttribute']); + + return $data; + } + + /** + * @depends testCreateStringAttribute + * @throws Exception + */ + public function testUpdateStringAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_STRING_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'name', + 'required' => false, + 'default' => 'Default Value', + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateStringAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateStringAttribute']['required']); + $this->assertEquals('Default Value', $column['body']['data']['collectionsUpdateStringAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateIntegerAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_INTEGER_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'age', + 'min' => 18, + 'max' => 150, + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateIntegerAttribute']); + + return $data; + } + + /** + * @depends testCreateIntegerAttribute + * @throws Exception + */ + public function testUpdateIntegerAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_INTEGER_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'age', + 'required' => false, + 'min' => 12, + 'max' => 160, + 'default' => 50 + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateIntegerAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateIntegerAttribute']['required']); + $this->assertEquals(12, $column['body']['data']['collectionsUpdateIntegerAttribute']['min']); + $this->assertEquals(160, $column['body']['data']['collectionsUpdateIntegerAttribute']['max']); + $this->assertEquals(50, $column['body']['data']['collectionsUpdateIntegerAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateBooleanAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_BOOLEAN_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'alive', + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateBooleanAttribute']); + + return $data; + } + + /** + * @depends testCreateBooleanAttribute + * @throws Exception + */ + public function testUpdateBooleanAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_BOOLEAN_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'alive', + 'required' => false, + 'default' => true + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateBooleanAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateBooleanAttribute']['required']); + $this->assertTrue($column['body']['data']['collectionsUpdateBooleanAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateFloatAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_FLOAT_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'salary', + 'min' => 1000.0, + 'max' => 999999.99, + 'default' => 1000.0, + 'required' => false, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateFloatAttribute']); + + return $data; + } + + /** + * @depends testCreateFloatAttribute + * @throws Exception + */ + public function testUpdateFloatAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_FLOAT_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'salary', + 'required' => false, + 'min' => 100.0, + 'max' => 1000000.0, + 'default' => 2500.0 + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateFloatAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateFloatAttribute']['required']); + $this->assertEquals(100.0, $column['body']['data']['collectionsUpdateFloatAttribute']['min']); + $this->assertEquals(1000000.0, $column['body']['data']['collectionsUpdateFloatAttribute']['max']); + $this->assertEquals(2500.0, $column['body']['data']['collectionsUpdateFloatAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateEmailAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_EMAIL_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'email', + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateEmailAttribute']); + + return $data; + } + + /** + * @depends testCreateEmailAttribute + * @throws Exception + */ + public function testUpdateEmailAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_EMAIL_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'email', + 'required' => false, + 'default' => 'torsten@appwrite.io', + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateEmailAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateEmailAttribute']['required']); + $this->assertEquals('torsten@appwrite.io', $column['body']['data']['collectionsUpdateEmailAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateEnumAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_ENUM_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'role', + 'elements' => [ + 'crew', + 'actor', + 'guest', + ], + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateEnumAttribute']); + + return $data; + } + + + /** + * @depends testCreateEnumAttribute + * @throws Exception + */ + public function testUpdateEnumAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_ENUM_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'role', + 'required' => false, + 'elements' => [ + 'crew', + 'tech', + 'actor' + ], + 'default' => 'tech' + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateEnumAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateEnumAttribute']['required']); + $this->assertEquals('tech', $column['body']['data']['collectionsUpdateEnumAttribute']['default']); + $this->assertContains('tech', $column['body']['data']['collectionsUpdateEnumAttribute']['elements']); + $this->assertNotContains('guest', $column['body']['data']['collectionsUpdateEnumAttribute']['elements']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateDatetimeAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_DATETIME_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'dob', + 'required' => true, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateDatetimeAttribute']); + + return $data; + } + + /** + * @depends testCreateDatetimeAttribute + * @throws Exception + */ + public function testUpdateDatetimeAttribute($data): array + { + // Wait for attributes to be available + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_DATETIME_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'dob', + 'required' => false, + 'default' => '2000-01-01T00:00:00Z' + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateDatetimeAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateDatetimeAttribute']['required']); + $this->assertEquals('2000-01-01T00:00:00Z', $column['body']['data']['collectionsUpdateDatetimeAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + */ + public function testCreateRelationshipAttribute(array $data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_RELATIONSHIP_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['collection2']['_id'], // Movies + 'relatedCollectionId' => $data['table']['_id'], // Actors + 'type' => Database::RELATION_ONE_TO_MANY, + 'twoWay' => true, + 'key' => 'actors', + 'twoWayKey' => 'movie' + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateRelationshipAttribute']); + + return $data; + } + + /** + * @depends testCreateRelationshipAttribute + */ + public function testUpdateRelationshipAttribute(array $data): array + { + sleep(1); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['collection2']['_id'], + 'key' => 'actors', + 'onDelete' => Database::RELATION_MUTATE_CASCADE, + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateRelationshipAttribute']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateIPAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_IP_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'ip', + 'required' => false, + 'default' => '::1', + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateIpAttribute']); + + return $data; + } + + /** + * @depends testCreateIPAttribute + * @throws Exception + */ + public function testUpdateIPAttribute($data): array + { + // Wait for attributes to be available + sleep(3); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_IP_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'ip', + 'required' => false, + 'default' => '127.0.0.1' + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateIpAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateIpAttribute']['required']); + $this->assertEquals('127.0.0.1', $column['body']['data']['collectionsUpdateIpAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + + return $data; + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testCreateURLAttribute($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_URL_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'url', + 'required' => false, + 'default' => 'https://appwrite.io', + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsCreateUrlAttribute']); + + return $data; + } + + /** + * @depends testCreateURLAttribute + * @throws Exception + */ + public function testUpdateURLAttribute($data): void + { + // Wait for attributes to be available + sleep(3); + + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_URL_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'url', + 'required' => false, + 'default' => 'https://cloud.appwrite.io' + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsUpdateUrlAttribute']); + $this->assertFalse($column['body']['data']['collectionsUpdateUrlAttribute']['required']); + $this->assertEquals('https://cloud.appwrite.io', $column['body']['data']['collectionsUpdateUrlAttribute']['default']); + $this->assertEquals(200, $column['headers']['status-code']); + } + + /** + * @depends testUpdateStringAttribute + * @depends testUpdateIntegerAttribute + * @throws Exception + */ + public function testCreateIndex($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_INDEX); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'index', + 'type' => 'key', + 'attributes' => [ + 'name', + 'age', + ], + ] + ]; + + $index = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $index['body']); + $this->assertIsArray($index['body']['data']); + $this->assertIsArray($index['body']['data']['collectionsCreateIndex']); + + return [ + 'database' => $data['database'], + 'table' => $data['table'], + 'index' => $index['body']['data']['collectionsCreateIndex'], + ]; + } + + /** + * @depends testUpdateStringAttribute + * @depends testUpdateIntegerAttribute + * @depends testUpdateBooleanAttribute + * @depends testUpdateEnumAttribute + * @throws Exception + */ + public function testCreateDocument($data): array + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$CREATE_DOCUMENT); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => ID::unique(), + 'data' => [ + 'name' => 'John Doe', + 'email' => 'example@appwrite.io', + 'age' => 30, + 'alive' => true, + 'salary' => 9999.9, + 'role' => 'crew', + 'dob' => '2000-01-01T00:00:00Z', + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); + + $row = $row['body']['data']['collectionsCreateDocument']; + $this->assertIsArray($row); + + return [ + 'database' => $data['database'], + 'table' => $data['table'], + 'row' => $row, + ]; + } + + // /** + // * @depends testCreateStringAttribute + // * @depends testCreateIntegerAttribute + // * @depends testCreateBooleanAttribute + // * @depends testCreateFloatAttribute + // * @depends testCreateEmailAttribute + // * @depends testCreateEnumAttribute + // * @depends testCreateDatetimeAttribute + // * @throws Exception + // */ + // public function testCreateCustomEntity(): array + // { + // $projectId = $this->getProject()['$id']; + // $query = $this->getQuery(self::$CREATE_CUSTOM_ENTITY); + // $gqlPayload = [ + // 'query' => $query, + // 'variables' => [ + // 'name' => 'John Doe', + // 'age' => 35, + // 'alive' => true, + // 'salary' => 9999.9, + // 'email' => 'johndoe@appwrite.io', + // 'role' => 'crew', + // 'dob' => '2000-01-01T00:00:00Z', + // ] + // ]; + // + // $actor = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $projectId, + // ], $this->getHeaders()), $gqlPayload); + // + // $this->assertArrayNotHasKey('errors', $actor['body']); + // $this->assertIsArray($actor['body']['data']); + // $actor = $actor['body']['data']['actorsCreate']; + // $this->assertIsArray($actor); + // + // return $actor; + // } + + public function testGetDatabases(): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_DATABASES); + $gqlPayload = [ + 'query' => $query, + ]; + + $databases = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $databases['body']); + $this->assertIsArray($databases['body']['data']); + $this->assertIsArray($databases['body']['data']['databasesList']); + } + + /** + * @depends testCreateDatabase + * @throws Exception + */ + public function testGetDatabase($database): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $database['_id'], + ] + ]; + + $database = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $database['body']); + $this->assertIsArray($database['body']['data']); + $this->assertIsArray($database['body']['data']['databasesGet']); + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testGetCollections($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_COLLECTIONS); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + ] + ]; + + $tables = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $tables['body']); + $this->assertIsArray($tables['body']['data']); + $this->assertIsArray($tables['body']['data']['databasesListCollections']); + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testGetCollection($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_COLLECTION); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + ] + ]; + + $table = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $table['body']); + $this->assertIsArray($table['body']['data']); + $this->assertIsArray($table['body']['data']['databasesGetCollection']); + } + + /** + * @depends testUpdateStringAttribute + * @depends testUpdateIntegerAttribute + * @throws Exception + */ + public function testGetAttributes($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_ATTRIBUTES); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + ] + ]; + + $columns = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $columns['body']); + $this->assertIsArray($columns['body']['data']); + $this->assertIsArray($columns['body']['data']['collectionsListAttributes']); + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testGetAttribute($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'name', + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $column['body']); + $this->assertIsArray($column['body']['data']); + $this->assertIsArray($column['body']['data']['collectionsGetAttribute']); + } + + /** + * @depends testCreateIndex + * @throws Exception + */ + public function testGetIndexes($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_INDEXES); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + ] + ]; + + $indices = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $indices['body']); + $this->assertIsArray($indices['body']['data']); + $this->assertIsArray($indices['body']['data']['collectionsListIndexes']); + } + + /** + * @depends testCreateIndex + * @throws Exception + */ + public function testGetIndex($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_INDEX); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => $data['index']['key'], + ] + ]; + + $index = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $index['body']); + $this->assertIsArray($index['body']['data']); + $this->assertIsArray($index['body']['data']['collectionsGetIndex']); + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testGetDocuments($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_DOCUMENTS); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + ] + ]; + + $rows = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $rows['body']); + $this->assertIsArray($rows['body']['data']); + $this->assertIsArray($rows['body']['data']['collectionsListDocuments']); + } + + /** + * @depends testCreateDocument + * @throws Exception + */ + public function testGetDocument($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$GET_DOCUMENT); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); + $this->assertIsArray($row['body']['data']['collectionsGetDocument']); + } + + // /** + // * @depends testCreateCustomEntity + // * @throws Exception + // */ + // public function testGetCustomEntities($data) + // { + // $projectId = $this->getProject()['$id']; + // $query = $this->getQuery(self::$GET_CUSTOM_ENTITIES); + // $gqlPayload = [ + // 'query' => $query, + // ]; + // + // $customEntities = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $projectId, + // ], $this->getHeaders()), $gqlPayload); + // + // $this->assertArrayNotHasKey('errors', $customEntities['body']); + // $this->assertIsArray($customEntities['body']['data']); + // $this->assertIsArray($customEntities['body']['data']['actorsList']); + // } + // + // /** + // * @depends testCreateCustomEntity + // * @throws Exception + // */ + // public function testGetCustomEntity($data) + // { + // $projectId = $this->getProject()['$id']; + // $query = $this->getQuery(self::$GET_CUSTOM_ENTITY); + // $gqlPayload = [ + // 'query' => $query, + // 'variables' => [ + // 'id' => $data['id'], + // ] + // ]; + // + // $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $projectId, + // ], $this->getHeaders()), $gqlPayload); + // + // $this->assertArrayNotHasKey('errors', $entity['body']); + // $this->assertIsArray($entity['body']['data']); + // $this->assertIsArray($entity['body']['data']['actorsGet']); + // } + + /** + * @depends testCreateDatabase + * @throws Exception + */ + public function testUpdateDatabase($database) + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $database['_id'], + 'name' => 'New Database Name', + ] + ]; + + $database = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $database['body']); + $this->assertIsArray($database['body']['data']); + $this->assertIsArray($database['body']['data']['databasesUpdate']); + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testUpdateCollection($data) + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_COLLECTION); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'name' => 'New Collection Name', + 'rowSecurity' => false, + ] + ]; + + $table = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $table['body']); + $this->assertIsArray($table['body']['data']); + $this->assertIsArray($table['body']['data']['databasesUpdateCollection']); + } + + /** + * @depends testCreateDocument + * @throws Exception + */ + public function testUpdateDocument($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$UPDATE_DOCUMENT); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], + 'data' => [ + 'name' => 'New Document Name', + ], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertArrayNotHasKey('errors', $row['body']); + $this->assertIsArray($row['body']['data']); + $row = $row['body']['data']['collectionsUpdateDocument']; + $this->assertIsArray($row); + $this->assertStringContainsString('New Document Name', $row['data']); + } + + // /** + // * @depends testCreateCustomEntity + // * @throws Exception + // */ + // public function testUpdateCustomEntity(array $data) + // { + // $projectId = $this->getProject()['$id']; + // $query = $this->getQuery(self::$UPDATE_CUSTOM_ENTITY); + // $gqlPayload = [ + // 'query' => $query, + // 'variables' => [ + // 'id' => $data['id'], + // 'name' => 'New Custom Entity Name', + // ] + // ]; + // + // $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $projectId, + // ], $this->getHeaders()), $gqlPayload); + // + // $this->assertArrayNotHasKey('errors', $entity['body']); + // $this->assertIsArray($entity['body']['data']); + // $entity = $entity['body']['data']['actorsUpdate']; + // $this->assertIsArray($entity); + // $this->assertStringContainsString('New Custom Entity Name', $entity['name']); + // } + + /** + * @depends testCreateDocument + * @throws Exception + */ + public function testDeleteDocument($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$DELETE_DOCUMENT); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'rowId' => $data['row']['_id'], + ] + ]; + + $row = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsNotArray($row['body']); + $this->assertEquals(204, $row['headers']['status-code']); + } + + // /** + // * @depends testCreateCustomEntity + // * @throws Exception + // */ + // public function testDeleteCustomEntity(array $data) + // { + // $projectId = $this->getProject()['$id']; + // $query = $this->getQuery(self::$DELETE_CUSTOM_ENTITY); + // $gqlPayload = [ + // 'query' => $query, + // 'variables' => [ + // 'id' => $data['id'], + // ] + // ]; + // + // $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $projectId, + // ], $this->getHeaders()), $gqlPayload); + // + // $this->assertIsNotArray($entity['body']); + // $this->assertEquals(204, $entity['headers']['status-code']); + // } + + /** + * @depends testUpdateStringAttribute + * @throws Exception + */ + public function testDeleteAttribute($data): void + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$DELETE_ATTRIBUTE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + 'key' => 'name', + ] + ]; + + $column = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsNotArray($column['body']); + $this->assertEquals(204, $column['headers']['status-code']); + } + + /** + * @depends testCreateCollection + * @throws Exception + */ + public function testDeleteCollection($data) + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$DELETE_COLLECTION); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['database']['_id'], + 'tableId' => $data['table']['_id'], + ] + ]; + + $table = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsNotArray($table['body']); + $this->assertEquals(204, $table['headers']['status-code']); + } + + /** + * @depends testCreateDatabase + * @throws Exception + */ + public function testDeleteDatabase($database) + { + $projectId = $this->getProject()['$id']; + $query = $this->getQuery(self::$DELETE_DATABASE); + $gqlPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $database['_id'], + ] + ]; + + $database = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), $gqlPayload); + + $this->assertIsNotArray($database['body']); + $this->assertEquals(204, $database['headers']['status-code']); + } +} From d1ae06550fe317109672d50888d39bd460be042e Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:09:43 +0530 Subject: [PATCH 094/173] update: realtime events logic. --- src/Appwrite/Event/Realtime.php | 70 +++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index f45b6548d8..016006ae73 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -72,32 +72,11 @@ class Realtime extends Event return false; } - $allEvents = Event::generateEvents($this->getEvent(), $this->getParams()); - $firstEvent = $allEvents[0]; // most verbose event pattern + $events = Event::generateEvents($this->getEvent(), $this->getParams()); + $firstEvent = $events[0]; // most verbose event pattern // generate and merge all collection and tables api events. - if (str_contains($this->getEvent(), 'databases.') && str_contains($firstEvent, 'collections')) { - $tableEventMap = [ - 'collections' => 'tables', 'attributes' => 'columns', - 'attributeId' => 'columnId', 'documents' => 'rows', 'documentId' => 'rowId', - ]; - - // replace params! - $tableEvent = str_replace( - array_keys($tableEventMap), - array_values($tableEventMap), - $this->getEvent() - ); - - // generate new events - $tableEvents = Event::generateEvents($tableEvent, $this->getParams()); - - // merge all of the api events - $allEvents = array_merge($allEvents, $tableEvents); - - // remove duplicates - $allEvents = array_values(array_unique($allEvents)); - } + $events = $this->mirrorCollectionEvents($firstEvent, $events); $payload = new Document($this->getPayload()); @@ -124,7 +103,7 @@ class Realtime extends Event $this->realtime->send( projectId: $projectId, payload: $this->getRealtimePayload(), - events: $allEvents, + events: $events, channels: $target['channels'], roles: $target['roles'], options: [ @@ -136,4 +115,45 @@ class Realtime extends Event return true; } + + /** + * Adds `table` events for `collection` events. + * + * Example: + * + * `databases.*.collections.*.documents.*.update` →\ + * `[databases.*.collections.*.documents.*.update, databases.*.tables.*.rows.*.update]` + */ + private function mirrorCollectionEvents(string $firstEvent, array $events): array + { + $tableEventMap = [ + 'documents' => 'rows', + 'collections' => 'tables', + 'attributes' => 'columns', + ]; + + if ( + str_contains($this->getEvent(), 'databases.') && + str_contains($firstEvent, 'collections') + ) { + $pairedEvents = []; + + foreach ($events as $event) { + $pairedEvents[] = $event; + + if (str_contains($event, 'collections')) { + $tableSideEvent = str_replace( + array_keys($tableEventMap), + array_values($tableEventMap), + $event + ); + $pairedEvents[] = $tableSideEvent; + } + } + + $events = $pairedEvents; + } + + return $events; + } } From 02af05b409e0e4dbdeb51d9b0ed86cc462ebde42 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:10:03 +0530 Subject: [PATCH 095/173] fix: endpoint in abuse test. --- tests/e2e/General/AbuseTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/General/AbuseTest.php b/tests/e2e/General/AbuseTest.php index 217f9f93ed..cfd12b0e9f 100644 --- a/tests/e2e/General/AbuseTest.php +++ b/tests/e2e/General/AbuseTest.php @@ -157,7 +157,7 @@ class AbuseTest extends Scope $collectionId = $data['collectionId']; $max = 120; - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows', [ + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], @@ -168,10 +168,10 @@ class AbuseTest extends Scope ], ]); - $documentId = $document['body']['$id']; + $rowId = $row['body']['$id']; for ($i = 0; $i <= $max + 1; $i++) { - $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [ + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $collectionId . '/rows/' . $rowId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ From 594f8595aea8d701913f4dc2a748304c5b39b007 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:10:23 +0530 Subject: [PATCH 096/173] update: database server test for graphql <> tables api. --- .../GraphQL/Tables/DatabaseServerTest.php | 380 +++++++++--------- 1 file changed, 190 insertions(+), 190 deletions(-) diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php index d5980a4096..4f711148e3 100644 --- a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php @@ -47,10 +47,10 @@ class DatabaseServerTest extends Scope /** * @depends testCreateDatabase */ - public function testCreateCollection($database): array + public function testCreateTable($database): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_COLLECTION); + $query = $this->getQuery(self::$CREATE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -74,7 +74,7 @@ class DatabaseServerTest extends Scope $this->assertIsArray($table['body']['data']); $this->assertArrayNotHasKey('errors', $table['body']); - $table = $table['body']['data']['databasesCreateCollection']; + $table = $table['body']['data']['databasesCreateTable']; $this->assertEquals('Actors', $table['name']); $gqlPayload = [ @@ -100,24 +100,24 @@ class DatabaseServerTest extends Scope $this->assertIsArray($table2['body']['data']); $this->assertArrayNotHasKey('errors', $table2['body']); - $table2 = $table2['body']['data']['databasesCreateCollection']; + $table2 = $table2['body']['data']['databasesCreateTable']; $this->assertEquals('Movies', $table2['name']); return [ 'database' => $database, 'table' => $table, - 'collection2' => $table2, + 'table2' => $table2, ]; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateStringAttribute($data): array + public function testCreateStringColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -136,22 +136,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateStringAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateStringColumn']); return $data; } /** - * @depends testCreateStringAttribute + * @depends testCreateStringColumn * @throws Exception */ - public function testUpdateStringAttribute($data): array + public function testUpdateStringColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -169,22 +169,22 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateStringAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateStringAttribute']['required']); - $this->assertEquals('Default Value', $column['body']['data']['collectionsUpdateStringAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateStringColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateStringColumn']['required']); + $this->assertEquals('Default Value', $column['body']['data']['tablesUpdateStringColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateIntegerAttribute($data): array + public function testCreateIntegerColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_INTEGER_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -204,22 +204,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateIntegerAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateIntegerColumn']); return $data; } /** - * @depends testCreateIntegerAttribute + * @depends testCreateIntegerColumn * @throws Exception */ - public function testUpdateIntegerAttribute($data): array + public function testUpdateIntegerColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_INTEGER_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -239,24 +239,24 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateIntegerAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateIntegerAttribute']['required']); - $this->assertEquals(12, $column['body']['data']['collectionsUpdateIntegerAttribute']['min']); - $this->assertEquals(160, $column['body']['data']['collectionsUpdateIntegerAttribute']['max']); - $this->assertEquals(50, $column['body']['data']['collectionsUpdateIntegerAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateIntegerColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateIntegerColumn']['required']); + $this->assertEquals(12, $column['body']['data']['tablesUpdateIntegerColumn']['min']); + $this->assertEquals(160, $column['body']['data']['tablesUpdateIntegerColumn']['max']); + $this->assertEquals(50, $column['body']['data']['tablesUpdateIntegerColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateBooleanAttribute($data): array + public function testCreateBooleanColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_BOOLEAN_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_BOOLEAN_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -274,22 +274,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateBooleanAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateBooleanColumn']); return $data; } /** - * @depends testCreateBooleanAttribute + * @depends testCreateBooleanColumn * @throws Exception */ - public function testUpdateBooleanAttribute($data): array + public function testUpdateBooleanColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_BOOLEAN_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_BOOLEAN_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -307,22 +307,22 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateBooleanAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateBooleanAttribute']['required']); - $this->assertTrue($column['body']['data']['collectionsUpdateBooleanAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateBooleanColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateBooleanColumn']['required']); + $this->assertTrue($column['body']['data']['tablesUpdateBooleanColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateFloatAttribute($data): array + public function testCreateFloatColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_FLOAT_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_FLOAT_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -343,22 +343,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateFloatAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateFloatColumn']); return $data; } /** - * @depends testCreateFloatAttribute + * @depends testCreateFloatColumn * @throws Exception */ - public function testUpdateFloatAttribute($data): array + public function testUpdateFloatColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_FLOAT_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_FLOAT_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -378,24 +378,24 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateFloatAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateFloatAttribute']['required']); - $this->assertEquals(100.0, $column['body']['data']['collectionsUpdateFloatAttribute']['min']); - $this->assertEquals(1000000.0, $column['body']['data']['collectionsUpdateFloatAttribute']['max']); - $this->assertEquals(2500.0, $column['body']['data']['collectionsUpdateFloatAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateFloatColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateFloatColumn']['required']); + $this->assertEquals(100.0, $column['body']['data']['tablesUpdateFloatColumn']['min']); + $this->assertEquals(1000000.0, $column['body']['data']['tablesUpdateFloatColumn']['max']); + $this->assertEquals(2500.0, $column['body']['data']['tablesUpdateFloatColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateEmailAttribute($data): array + public function testCreateEmailColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_EMAIL_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_EMAIL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -413,22 +413,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateEmailAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateEmailColumn']); return $data; } /** - * @depends testCreateEmailAttribute + * @depends testCreateEmailColumn * @throws Exception */ - public function testUpdateEmailAttribute($data): array + public function testUpdateEmailColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_EMAIL_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_EMAIL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -446,22 +446,22 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateEmailAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateEmailAttribute']['required']); - $this->assertEquals('torsten@appwrite.io', $column['body']['data']['collectionsUpdateEmailAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateEmailColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateEmailColumn']['required']); + $this->assertEquals('torsten@appwrite.io', $column['body']['data']['tablesUpdateEmailColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateEnumAttribute($data): array + public function testCreateEnumColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_ENUM_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_ENUM_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -484,23 +484,23 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateEnumAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateEnumColumn']); return $data; } /** - * @depends testCreateEnumAttribute + * @depends testCreateEnumColumn * @throws Exception */ - public function testUpdateEnumAttribute($data): array + public function testUpdateEnumColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_ENUM_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_ENUM_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -523,24 +523,24 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateEnumAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateEnumAttribute']['required']); - $this->assertEquals('tech', $column['body']['data']['collectionsUpdateEnumAttribute']['default']); - $this->assertContains('tech', $column['body']['data']['collectionsUpdateEnumAttribute']['elements']); - $this->assertNotContains('guest', $column['body']['data']['collectionsUpdateEnumAttribute']['elements']); + $this->assertIsArray($column['body']['data']['tablesUpdateEnumColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateEnumColumn']['required']); + $this->assertEquals('tech', $column['body']['data']['tablesUpdateEnumColumn']['default']); + $this->assertContains('tech', $column['body']['data']['tablesUpdateEnumColumn']['elements']); + $this->assertNotContains('guest', $column['body']['data']['tablesUpdateEnumColumn']['elements']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateDatetimeAttribute($data): array + public function testCreateDatetimeColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DATETIME_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_DATETIME_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -558,22 +558,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateDatetimeAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateDatetimeColumn']); return $data; } /** - * @depends testCreateDatetimeAttribute + * @depends testCreateDatetimeColumn * @throws Exception */ - public function testUpdateDatetimeAttribute($data): array + public function testUpdateDatetimeColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_DATETIME_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_DATETIME_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -591,27 +591,27 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateDatetimeAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateDatetimeAttribute']['required']); - $this->assertEquals('2000-01-01T00:00:00Z', $column['body']['data']['collectionsUpdateDatetimeAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateDatetimeColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateDatetimeColumn']['required']); + $this->assertEquals('2000-01-01T00:00:00Z', $column['body']['data']['tablesUpdateDatetimeColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable */ - public function testCreateRelationshipAttribute(array $data): array + public function testCreateRelationshipColumn(array $data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_RELATIONSHIP_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_RELATIONSHIP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['collection2']['_id'], // Movies - 'relatedCollectionId' => $data['table']['_id'], // Actors + 'tableId' => $data['table2']['_id'], // Movies + 'relatedTableId' => $data['table']['_id'], // Actors 'type' => Database::RELATION_ONE_TO_MANY, 'twoWay' => true, 'key' => 'actors', @@ -626,25 +626,25 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateRelationshipAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateRelationshipColumn']); return $data; } /** - * @depends testCreateRelationshipAttribute + * @depends testCreateRelationshipColumn */ - public function testUpdateRelationshipAttribute(array $data): array + public function testUpdateRelationshipColumn(array $data): array { sleep(1); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_RELATIONSHIP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], - 'tableId' => $data['collection2']['_id'], + 'tableId' => $data['table2']['_id'], 'key' => 'actors', 'onDelete' => Database::RELATION_MUTATE_CASCADE, ] @@ -657,19 +657,19 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateRelationshipAttribute']); + $this->assertIsArray($column['body']['data']['tablesUpdateRelationshipColumn']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateIPAttribute($data): array + public function testCreateIPColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_IP_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_IP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -688,22 +688,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateIpAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateIpColumn']); return $data; } /** - * @depends testCreateIPAttribute + * @depends testCreateIPColumn * @throws Exception */ - public function testUpdateIPAttribute($data): array + public function testUpdateIPColumn($data): array { - // Wait for attributes to be available + // Wait for columns to be available sleep(3); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_IP_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_IP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -721,22 +721,22 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateIpAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateIpAttribute']['required']); - $this->assertEquals('127.0.0.1', $column['body']['data']['collectionsUpdateIpAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateIpColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateIpColumn']['required']); + $this->assertEquals('127.0.0.1', $column['body']['data']['tablesUpdateIpColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); return $data; } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testCreateURLAttribute($data): array + public function testCreateURLColumn($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_URL_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_URL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -755,22 +755,22 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsCreateUrlAttribute']); + $this->assertIsArray($column['body']['data']['tablesCreateUrlColumn']); return $data; } /** - * @depends testCreateURLAttribute + * @depends testCreateURLColumn * @throws Exception */ - public function testUpdateURLAttribute($data): void + public function testUpdateURLColumn($data): void { - // Wait for attributes to be available + // Wait for columns to be available sleep(3); $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_URL_ATTRIBUTE); + $query = $this->getQuery(self::$UPDATE_URL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -788,21 +788,21 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsUpdateUrlAttribute']); - $this->assertFalse($column['body']['data']['collectionsUpdateUrlAttribute']['required']); - $this->assertEquals('https://cloud.appwrite.io', $column['body']['data']['collectionsUpdateUrlAttribute']['default']); + $this->assertIsArray($column['body']['data']['tablesUpdateUrlColumn']); + $this->assertFalse($column['body']['data']['tablesUpdateUrlColumn']['required']); + $this->assertEquals('https://cloud.appwrite.io', $column['body']['data']['tablesUpdateUrlColumn']['default']); $this->assertEquals(200, $column['headers']['status-code']); } /** - * @depends testUpdateStringAttribute - * @depends testUpdateIntegerAttribute + * @depends testUpdateStringColumn + * @depends testUpdateIntegerColumn * @throws Exception */ public function testCreateIndex($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_INDEX); + $query = $this->getQuery(self::$CREATE_COLUMN_INDEX); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -810,7 +810,7 @@ class DatabaseServerTest extends Scope 'tableId' => $data['table']['_id'], 'key' => 'index', 'type' => 'key', - 'attributes' => [ + 'columns' => [ 'name', 'age', ], @@ -824,26 +824,26 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $index['body']); $this->assertIsArray($index['body']['data']); - $this->assertIsArray($index['body']['data']['collectionsCreateIndex']); + $this->assertIsArray($index['body']['data']['tablesCreateIndex']); return [ 'database' => $data['database'], 'table' => $data['table'], - 'index' => $index['body']['data']['collectionsCreateIndex'], + 'index' => $index['body']['data']['tablesCreateIndex'], ]; } /** - * @depends testUpdateStringAttribute - * @depends testUpdateIntegerAttribute - * @depends testUpdateBooleanAttribute - * @depends testUpdateEnumAttribute + * @depends testUpdateStringColumn + * @depends testUpdateIntegerColumn + * @depends testUpdateBooleanColumn + * @depends testUpdateEnumColumn * @throws Exception */ - public function testCreateDocument($data): array + public function testCreateRow($data): array { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$CREATE_DOCUMENT); + $query = $this->getQuery(self::$CREATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -875,7 +875,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $row['body']); $this->assertIsArray($row['body']['data']); - $row = $row['body']['data']['collectionsCreateDocument']; + $row = $row['body']['data']['tablesCreateRow']; $this->assertIsArray($row); return [ @@ -886,13 +886,13 @@ class DatabaseServerTest extends Scope } // /** - // * @depends testCreateStringAttribute - // * @depends testCreateIntegerAttribute - // * @depends testCreateBooleanAttribute - // * @depends testCreateFloatAttribute - // * @depends testCreateEmailAttribute - // * @depends testCreateEnumAttribute - // * @depends testCreateDatetimeAttribute + // * @depends testCreateStringColumn + // * @depends testCreateIntegerColumn + // * @depends testCreateBooleanColumn + // * @depends testCreateFloatColumn + // * @depends testCreateEmailColumn + // * @depends testCreateEnumColumn + // * @depends testCreateDatetimeColumn // * @throws Exception // */ // public function testCreateCustomEntity(): array @@ -969,13 +969,13 @@ class DatabaseServerTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testGetCollections($data): void + public function testGetTables($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_COLLECTIONS); + $query = $this->getQuery(self::$GET_TABLES); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -990,17 +990,17 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $tables['body']); $this->assertIsArray($tables['body']['data']); - $this->assertIsArray($tables['body']['data']['databasesListCollections']); + $this->assertIsArray($tables['body']['data']['databasesListTables']); } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testGetCollection($data): void + public function testGetTable($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_COLLECTION); + $query = $this->getQuery(self::$GET_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1016,18 +1016,18 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $table['body']); $this->assertIsArray($table['body']['data']); - $this->assertIsArray($table['body']['data']['databasesGetCollection']); + $this->assertIsArray($table['body']['data']['databasesGetTable']); } /** - * @depends testUpdateStringAttribute - * @depends testUpdateIntegerAttribute + * @depends testUpdateStringColumn + * @depends testUpdateIntegerColumn * @throws Exception */ - public function testGetAttributes($data): void + public function testGetColumns($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ATTRIBUTES); + $query = $this->getQuery(self::$GET_COLUMNS); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1043,17 +1043,17 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $columns['body']); $this->assertIsArray($columns['body']['data']); - $this->assertIsArray($columns['body']['data']['collectionsListAttributes']); + $this->assertIsArray($columns['body']['data']['tablesListColumns']); } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testGetAttribute($data): void + public function testGetColumn($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_ATTRIBUTE); + $query = $this->getQuery(self::$GET_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1070,7 +1070,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); - $this->assertIsArray($column['body']['data']['collectionsGetAttribute']); + $this->assertIsArray($column['body']['data']['tablesGetColumn']); } /** @@ -1080,7 +1080,7 @@ class DatabaseServerTest extends Scope public function testGetIndexes($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_INDEXES); + $query = $this->getQuery(self::$GET_COLUMN_INDEXES); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1096,7 +1096,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $indices['body']); $this->assertIsArray($indices['body']['data']); - $this->assertIsArray($indices['body']['data']['collectionsListIndexes']); + $this->assertIsArray($indices['body']['data']['tablesListIndexes']); } /** @@ -1106,7 +1106,7 @@ class DatabaseServerTest extends Scope public function testGetIndex($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_INDEX); + $query = $this->getQuery(self::$GET_COLUMN_INDEX); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1123,17 +1123,17 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $index['body']); $this->assertIsArray($index['body']['data']); - $this->assertIsArray($index['body']['data']['collectionsGetIndex']); + $this->assertIsArray($index['body']['data']['tablesGetIndex']); } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testGetDocuments($data): void + public function testGetRows($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_DOCUMENTS); + $query = $this->getQuery(self::$GET_ROWS); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1149,17 +1149,17 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $rows['body']); $this->assertIsArray($rows['body']['data']); - $this->assertIsArray($rows['body']['data']['collectionsListDocuments']); + $this->assertIsArray($rows['body']['data']['tablesListRows']); } /** - * @depends testCreateDocument + * @depends testCreateRow * @throws Exception */ - public function testGetDocument($data): void + public function testGetRow($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$GET_DOCUMENT); + $query = $this->getQuery(self::$GET_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1176,7 +1176,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $row['body']); $this->assertIsArray($row['body']['data']); - $this->assertIsArray($row['body']['data']['collectionsGetDocument']); + $this->assertIsArray($row['body']['data']['tablesGetRow']); } // /** @@ -1253,19 +1253,19 @@ class DatabaseServerTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testUpdateCollection($data) + public function testUpdateTable($data) { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_COLLECTION); + $query = $this->getQuery(self::$UPDATE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ 'databaseId' => $data['database']['_id'], 'tableId' => $data['table']['_id'], - 'name' => 'New Collection Name', + 'name' => 'New Table Name', 'rowSecurity' => false, ] ]; @@ -1277,17 +1277,17 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $table['body']); $this->assertIsArray($table['body']['data']); - $this->assertIsArray($table['body']['data']['databasesUpdateCollection']); + $this->assertIsArray($table['body']['data']['databasesUpdateTable']); } /** - * @depends testCreateDocument + * @depends testCreateRow * @throws Exception */ - public function testUpdateDocument($data): void + public function testUpdateRow($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$UPDATE_DOCUMENT); + $query = $this->getQuery(self::$UPDATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1295,7 +1295,7 @@ class DatabaseServerTest extends Scope 'tableId' => $data['table']['_id'], 'rowId' => $data['row']['_id'], 'data' => [ - 'name' => 'New Document Name', + 'name' => 'New Row Name', ], ] ]; @@ -1307,9 +1307,9 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $row['body']); $this->assertIsArray($row['body']['data']); - $row = $row['body']['data']['collectionsUpdateDocument']; + $row = $row['body']['data']['tablesUpdateRow']; $this->assertIsArray($row); - $this->assertStringContainsString('New Document Name', $row['data']); + $this->assertStringContainsString('New Row Name', $row['data']); } // /** @@ -1341,13 +1341,13 @@ class DatabaseServerTest extends Scope // } /** - * @depends testCreateDocument + * @depends testCreateRow * @throws Exception */ - public function testDeleteDocument($data): void + public function testDeleteRow($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_DOCUMENT); + $query = $this->getQuery(self::$DELETE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1391,13 +1391,13 @@ class DatabaseServerTest extends Scope // } /** - * @depends testUpdateStringAttribute + * @depends testUpdateStringColumn * @throws Exception */ - public function testDeleteAttribute($data): void + public function testDeleteColumn($data): void { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_ATTRIBUTE); + $query = $this->getQuery(self::$DELETE_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ @@ -1417,13 +1417,13 @@ class DatabaseServerTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws Exception */ - public function testDeleteCollection($data) + public function testDeleteTable($data) { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$DELETE_COLLECTION); + $query = $this->getQuery(self::$DELETE_TABLE); $gqlPayload = [ 'query' => $query, 'variables' => [ From 68994f263490e129ceeacda491be42f9e57331e2 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:19:01 +0530 Subject: [PATCH 097/173] fix: graphql tests. --- tests/e2e/Services/GraphQL/Base.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 65154e2f93..8cad67b909 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -82,17 +82,17 @@ trait Base public static string $GET_COLUMN = 'get_column'; public static string $DELETE_COLUMN = 'delete_column'; - // Collection Indexes - public static string $CREATE_INDEX = 'create_index'; - public static string $GET_INDEXES = 'get_indexes'; - public static string $GET_INDEX = 'get_index'; - public static string $DELETE_INDEX = 'delete_index'; + // Attribute Indexes + public static string $CREATE_INDEX = 'create_attribute_index'; + public static string $GET_INDEXES = 'get_attribute_indexes'; + public static string $GET_INDEX = 'get_attribute_index'; + public static string $DELETE_INDEX = 'delete_attribute_index'; // Column Indexes - public static string $CREATE_COLUMN_INDEX = 'create_index'; - public static string $GET_COLUMN_INDEXES = 'get_indexes'; - public static string $GET_COLUMN_INDEX = 'get_index'; - public static string $DELETE_COLUMN_INDEX = 'delete_index'; + public static string $CREATE_COLUMN_INDEX = 'create_column_index'; + public static string $GET_COLUMN_INDEXES = 'get_column_indexes'; + public static string $GET_COLUMN_INDEX = 'get_column_index'; + public static string $DELETE_COLUMN_INDEX = 'delete_column_index'; // Documents public static string $CREATE_DOCUMENT = 'create_document_rest'; @@ -995,8 +995,8 @@ trait Base } }'; case self::$CREATE_COLUMN_INDEX: - return 'mutation createIndex($databaseId: String!, $tableId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ - tablesCreateIndex(databaseId: $databaseId, tableId: $tableId, key: $key, type: $type, attributes: $attributes, orders: $orders) { + return 'mutation createIndex($databaseId: String!, $tableId: String!, $key: String!, $type: String!, $columns: [String!]!, $orders: [String!]){ + tablesCreateIndex(databaseId: $databaseId, tableId: $tableId, key: $key, type: $type, columns: $columns, orders: $orders) { key type status From 5022a052ce10ed4db669a2cf687615718bc48b7e Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:20:23 +0530 Subject: [PATCH 098/173] update: test coverage for databases. --- .github/workflows/tests.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 335e46c16b..a7fc1cf0c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,8 +145,7 @@ jobs: Account, Avatars, Console, - Databases/Collections, - Databases/Tables, + Databases, Functions, FunctionsSchedule, GraphQL, @@ -214,8 +213,7 @@ jobs: Account, Avatars, Console, - Databases/Collections, - Databases/Tables, + Databases, Functions, FunctionsSchedule, GraphQL, From fbe2609f7b9561b207ba96f8fa1721e6a6da763d Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:34:44 +0530 Subject: [PATCH 099/173] update: simplify setting events context param. --- .../Collections/Attributes/Action.php | 20 +++++++++++++------ .../Collections/Attributes/Delete.php | 9 ++++----- .../Collections/Documents/Action.php | 8 ++++++++ .../Collections/Documents/Create.php | 8 ++++---- .../Collections/Documents/Delete.php | 8 ++++---- .../Collections/Documents/Update.php | 8 ++++---- .../Databases/Collections/Indexes/Action.php | 8 ++++++++ .../Databases/Collections/Indexes/Create.php | 4 ++-- .../Databases/Collections/Indexes/Delete.php | 4 ++-- 9 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 954e997872..095b679223 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -204,6 +204,14 @@ abstract class Action extends UtopiaAction : Exception::COLUMN_NOT_AVAILABLE; } + /** + * Get the correct collections context for Events queue. + */ + final protected function getCollectionsEventsContext(): string + { + return $this->isCollectionsAPI() ? 'collection' : 'table'; + } + /** * Get the proper column/attribute type based on set context. */ @@ -407,11 +415,11 @@ abstract class Action extends UtopiaAction $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) ->setParam('attributeId', $attribute->getId()) ->setParam('columnId', $attribute->getId()) - ->setParam('tableId', $collection->getId()) - ->setParam('collectionId', $collection->getId()) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + ->setContext($this->getCollectionsEventsContext(), $collection); $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); @@ -608,11 +616,11 @@ abstract class Action extends UtopiaAction $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) ->setParam('attributeId', $attribute->getId()) ->setParam('columnId', $attribute->getId()) - ->setParam('tableId', $collection->getId()) - ->setParam('collectionId', $collection->getId()) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + ->setContext($this->getCollectionsEventsContext(), $collection); return $attribute; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 81bc77a9ba..cf5b7766b3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -145,13 +145,12 @@ class Delete extends Action $queueForEvents ->setContext('database', $db) ->setParam('databaseId', $databaseId) - ->setPayload($response->output($attribute, $model)) - ->setParam('attributeId', $attribute->getId()) - ->setParam('columnId', $attribute->getId()) ->setParam('collectionId', $collection->getId()) ->setParam('tableId', $collection->getId()) - // set proper context - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + ->setParam('attributeId', $attribute->getId()) + ->setParam('columnId', $attribute->getId()) + ->setPayload($response->output($attribute, $model)) + ->setContext($this->getCollectionsEventsContext(), $collection); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index a5b8fc5805..01ee2d9f84 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -127,4 +127,12 @@ abstract class Action extends UtopiaAction ? Exception::DOCUMENT_MISSING_PAYLOAD : Exception::ROW_MISSING_PAYLOAD; } + + /** + * Get the correct collections context for Events queue. + */ + final protected function getCollectionsEventsContext(): string + { + return $this->isCollectionsAPI() ? 'collection' : 'table'; + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index a8f1561f84..bd59c9ee35 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -309,11 +309,11 @@ class Create extends Action $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) - ->setParam('rowId', $document->getId()) - ->setParam('documentId', $document->getId()) - ->setParam('tableId', $collection->getId()) ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('rowId', $document->getId()) ->setPayload($response->getPayload(), sensitive: $relationships) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + ->setContext($this->getCollectionsEventsContext(), $collection); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 66e1b37320..c218e713bf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -164,11 +164,11 @@ class Delete extends Action $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) - ->setParam('rowId', $document->getId()) - ->setParam('documentId', $document->getId()) - ->setParam('tableId', $collection->getId()) ->setParam('collectionId', $collection->getId()) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection) + ->setParam('tableId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('rowId', $document->getId()) + ->setContext($this->getCollectionsEventsContext(), $collection) ->setPayload($response->output($document, $this->getResponseModel()), sensitive: $relationships); $response->noContent(); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 5bee2ee761..17da8e552d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -297,11 +297,11 @@ class Update extends Action $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) - ->setParam('rowId', $document->getId()) - ->setParam('documentId', $document->getId()) - ->setParam('tableId', $collection->getId()) ->setParam('collectionId', $collection->getId()) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection) + ->setParam('tableId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('rowId', $document->getId()) + ->setContext($this->getCollectionsEventsContext(), $collection) ->setPayload($response->getPayload(), sensitive: $relationships); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index 93459440f0..c1e3f61114 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -152,4 +152,12 @@ abstract class Action extends UtopiaAction ? Exception::ATTRIBUTE_NOT_AVAILABLE : Exception::COLUMN_NOT_AVAILABLE; } + + /** + * Get the correct collections context for Events queue. + */ + final protected function getCollectionsEventsContext(): string + { + return $this->isCollectionsAPI() ? 'collection' : 'table'; + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 014627e0a8..c18ab69b10 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -221,9 +221,9 @@ class Create extends Action ->setContext('database', $db) ->setParam('databaseId', $databaseId) ->setParam('indexId', $index->getId()) - ->setParam('tableId', $collection->getId()) ->setParam('collectionId', $collection->getId()) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + ->setParam('tableId', $collection->getId()) + ->setContext($this->getCollectionsEventsContext(), $collection); $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index f86cf7b11f..a208e059f6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -118,8 +118,8 @@ class Delete extends Action ->setParam('indexId', $index->getId()) ->setParam('tableId', $collection->getId()) ->setParam('collectionId', $collection->getId()) - ->setPayload($response->output($index, $this->getResponseModel())) - ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection); + ->setContext($this->getCollectionsEventsContext(), $collection) + ->setPayload($response->output($index, $this->getResponseModel())); $response->noContent(); } From 5dca1ff0d624b5b9378fef397269190a28238e81 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:36:16 +0530 Subject: [PATCH 100/173] update: fix test variable. --- .../Tables/DatabasesPermissionsMemberTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php index 7b0e9d8a64..32a97aeadf 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php @@ -136,9 +136,9 @@ class DatabasesPermissionsMemberTest extends Scope 'rowSecurity' => true, ]); $this->assertEquals(201, $public['headers']['status-code']); - $this->collections = ['public' => $public['body']['$id']]; + $this->tables = ['public' => $public['body']['$id']]; - $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->collections['public'] . '/columns/string', $this->getServerHeader(), [ + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->tables['public'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, @@ -157,9 +157,9 @@ class DatabasesPermissionsMemberTest extends Scope 'rowSecurity' => true, ]); $this->assertEquals(201, $private['headers']['status-code']); - $this->collections['private'] = $private['body']['$id']; + $this->tables['private'] = $private['body']['$id']; - $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->collections['private'] . '/columns/string', $this->getServerHeader(), [ + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->tables['private'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, @@ -173,9 +173,9 @@ class DatabasesPermissionsMemberTest extends Scope 'rowSecurity' => true, ]); $this->assertEquals(201, $private['headers']['status-code']); - $this->collections['doconly'] = $doconly['body']['$id']; + $this->tables['doconly'] = $doconly['body']['$id']; - $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->collections['doconly'] . '/columns/string', $this->getServerHeader(), [ + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $this->tables['doconly'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, @@ -186,7 +186,7 @@ class DatabasesPermissionsMemberTest extends Scope return [ 'users' => $this->users, - 'tables' => $this->collections, + 'tables' => $this->tables, 'databaseId' => $databaseId ]; } From 45370f91c3b93e0373d8982ae481de90bfafd638 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 10:52:43 +0530 Subject: [PATCH 101/173] add: table apis to webhook tests. --- tests/e2e/Services/Webhooks/WebhooksBase.php | 352 ++++++++++++++++++- 1 file changed, 351 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index c743810feb..b26f964479 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -36,7 +36,7 @@ trait WebhooksBase return base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true)); } - + // Collection APIs public function testCreateCollection(): array { /** @@ -389,6 +389,356 @@ trait WebhooksBase return $data; } + // Table APIs + public function testCreateTable(): array + { + /** + * Create database + */ + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Actors DB', + ]); + + $databaseId = $database['body']['$id']; + + /** + * Test for SUCCESS + */ + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Actors', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => true, + ]); + + $actorsId = $actors['body']['$id']; + + $this->assertEquals($actors['headers']['status-code'], 201); + $this->assertNotEmpty($actors['body']['$id']); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals($webhook['data']['name'], 'Actors'); + $this->assertIsArray($webhook['data']['$permissions']); + $this->assertCount(4, $webhook['data']['$permissions']); + + return array_merge(['actorsId' => $actorsId, 'databaseId' => $databaseId]); + } + + /** + * @depends testCreateTable + */ + public function testCreateColumns(array $data): array + { + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + $firstName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'firstName', + 'size' => 256, + 'required' => true, + ]); + + $lastName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'lastName', + 'size' => 256, + 'required' => true, + ]); + + $extra = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'extra', + 'size' => 64, + 'required' => false, + ]); + + $this->assertEquals($firstName['headers']['status-code'], 202); + $this->assertEquals($firstName['body']['key'], 'firstName'); + $this->assertEquals($lastName['headers']['status-code'], 202); + $this->assertEquals($lastName['body']['key'], 'lastName'); + $this->assertEquals($extra['headers']['status-code'], 202); + $this->assertEquals($extra['body']['key'], 'extra'); + + // wait for database worker to kick in + sleep(10); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertNotEmpty($webhook['data']['key']); + $this->assertEquals($webhook['data']['key'], 'extra'); + + $removed = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['actorsId'] . '/columns/' . $extra['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $removed['headers']['status-code']); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + // $this->assertEquals($webhook['method'], 'DELETE'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.columns.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.columns.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertNotEmpty($webhook['data']['key']); + $this->assertEquals($webhook['data']['key'], 'extra'); + + return $data; + } + + /** + * @depends testCreateColumns + */ + public function testCreateRow(array $data): array + { + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + /** + * Test for SUCCESS + */ + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'firstName' => 'Chris', + 'lastName' => 'Evans', + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $documentId = $row['body']['$id']; + + $this->assertEquals($row['headers']['status-code'], 201); + $this->assertNotEmpty($row['body']['$id']); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals($webhook['data']['firstName'], 'Chris'); + $this->assertEquals($webhook['data']['lastName'], 'Evans'); + $this->assertIsArray($webhook['data']['$permissions']); + $this->assertCount(3, $webhook['data']['$permissions']); + + $data['rowId'] = $row['body']['$id']; + + return $data; + } + + /** + * @depends testCreateRow + */ + public function testUpdateRow(array $data): array + { + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + /** + * Test for SUCCESS + */ + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $actorsId . '/rows/' . $data['rowId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'firstName' => 'Chris1', + 'lastName' => 'Evans2', + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $rowId = $document['body']['$id']; + + $this->assertEquals($document['headers']['status-code'], 200); + $this->assertNotEmpty($document['body']['$id']); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$rowId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$rowId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$rowId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$rowId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals($webhook['data']['firstName'], 'Chris1'); + $this->assertEquals($webhook['data']['lastName'], 'Evans2'); + $this->assertIsArray($webhook['data']['$permissions']); + $this->assertCount(3, $webhook['data']['$permissions']); + + return $data; + } + + /** + * @depends testCreateTable + */ + #[Retry(count: 1)] + public function testDeleteRow(array $data): array + { + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + /** + * Test for SUCCESS + */ + $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actorsId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'firstName' => 'Bradly', + 'lastName' => 'Cooper', + + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $rowId = $row['body']['$id']; + + $this->assertEquals($row['headers']['status-code'], 201); + $this->assertNotEmpty($row['body']['$id']); + + $row = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actorsId . '/rows/' . $row['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals($row['headers']['status-code'], 204); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.rows.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$rowId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.*.rows.{$rowId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$rowId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.rows.{$rowId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals($webhook['data']['firstName'], 'Bradly'); + $this->assertEquals($webhook['data']['lastName'], 'Cooper'); + $this->assertIsArray($webhook['data']['$permissions']); + $this->assertCount(3, $webhook['data']['$permissions']); + + return $data; + } public function testCreateStorageBucket(): array { From afa8341f9584cf347da93ac2f817e78af6a35a94 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:01:36 +0530 Subject: [PATCH 102/173] add: table apis to webhook custom server tests. --- .../Webhooks/WebhooksCustomServerTest.php | 351 ++++++++++++++---- 1 file changed, 269 insertions(+), 82 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index d2f132e960..6fe184b03f 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -19,6 +19,7 @@ class WebhooksCustomServerTest extends Scope use ProjectCustom; use SideServer; + // Collection APIs /** * @depends testCreateAttributes */ @@ -39,15 +40,15 @@ class WebhooksCustomServerTest extends Scope 'documentSecurity' => true, ]); - $this->assertEquals($actors['headers']['status-code'], 200); + $this->assertEquals(200, $actors['headers']['status-code']); $this->assertNotEmpty($actors['body']['$id']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -55,9 +56,9 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertTrue(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? '')); $this->assertNotEmpty($webhook['data']['$id']); - $this->assertEquals($webhook['data']['name'], 'Actors1'); + $this->assertEquals('Actors1', $webhook['data']['name']); $this->assertIsArray($webhook['data']['$permissions']); $this->assertCount(4, $webhook['data']['$permissions']); @@ -84,8 +85,8 @@ class WebhooksCustomServerTest extends Scope ]); $indexKey = $index['body']['key']; - $this->assertEquals($index['headers']['status-code'], 202); - $this->assertEquals($index['body']['key'], 'fullname'); + $this->assertEquals(202, $index['headers']['status-code']); + $this->assertEquals('fullname', $index['body']['key']); // wait for database worker to create index sleep(5); @@ -93,9 +94,9 @@ class WebhooksCustomServerTest extends Scope $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -105,7 +106,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertTrue(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? '')); // Remove index $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['actorsId'] . '/indexes/' . $index['body']['key'], array_merge([ @@ -119,8 +120,8 @@ class WebhooksCustomServerTest extends Scope $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); // $this->assertEquals($webhook['method'], 'DELETE'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -130,7 +131,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertTrue(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? '')); return $data; } @@ -172,7 +173,7 @@ class WebhooksCustomServerTest extends Scope $id = $actors['body']['$id']; - $this->assertEquals($actors['headers']['status-code'], 201); + $this->assertEquals(201, $actors['headers']['status-code']); $this->assertNotEmpty($actors['body']['$id']); $actors = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'], array_merge([ @@ -181,14 +182,14 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), []); - $this->assertEquals($actors['headers']['status-code'], 204); + $this->assertEquals(204, $actors['headers']['status-code']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("databases.{$databaseId}.collections.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -196,9 +197,195 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertTrue(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? '')); $this->assertNotEmpty($webhook['data']['$id']); - $this->assertEquals($webhook['data']['name'], 'Demo'); + $this->assertEquals('Demo', $webhook['data']['name']); + $this->assertIsArray($webhook['data']['$permissions']); + $this->assertCount(4, $webhook['data']['$permissions']); + + return []; + } + + // Table APIs + /** + * @depends testCreateColumns + */ + public function testUpdateTable($data): array + { + $id = $data['actorsId']; + $databaseId = $data['databaseId']; + + /** + * Test for SUCCESS + */ + $actors = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Actors1', + 'rowSecurity' => true, + ]); + + $this->assertEquals(200, $actors['headers']['status-code']); + $this->assertNotEmpty($actors['body']['$id']); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEmpty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals('Actors1', $webhook['data']['name']); + $this->assertIsArray($webhook['data']['$permissions']); + $this->assertCount(4, $webhook['data']['$permissions']); + + return array_merge(['actorsId' => $actors['body']['$id']]); + } + + /** + * @depends testCreateColumns + */ + public function testCreateDeleteColumnIndexes($data): array + { + $actorsId = $data['actorsId']; + $databaseId = $data['databaseId']; + + $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['actorsId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'fullname', + 'type' => 'key', + 'columns' => ['lastName', 'firstName'], + 'orders' => ['ASC', 'ASC'], + ]); + + $this->assertEquals(202, $index['headers']['status-code']); + $this->assertEquals('fullname', $index['body']['key']); + + // wait for database worker to create index + sleep(5); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertTrue(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? '')); + + // Remove index + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['actorsId'] . '/indexes/' . $index['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + // // wait for database worker to remove index + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + // $this->assertEquals($webhook['method'], 'DELETE'); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$actorsId}.indexes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertTrue(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? '')); + + return $data; + } + + public function testDeleteTable(): array + { + /** + * Create database + */ + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders()), [ + 'databaseId' => ID::unique(), + 'name' => 'Actors DB', + ]); + + $databaseId = $database['body']['$id']; + + /** + * Test for SUCCESS + */ + $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Demo', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => true, + ]); + + $id = $actors['body']['$id']; + + $this->assertEquals(201, $actors['headers']['status-code']); + $this->assertNotEmpty($actors['body']['$id']); + + $actors = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $actors['headers']['status-code']); + + $webhook = $this->getLastRequest(); + $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); + + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.tables.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.tables.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEmpty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals('Demo', $webhook['data']['name']); $this->assertIsArray($webhook['data']['$permissions']); $this->assertCount(4, $webhook['data']['$permissions']); @@ -224,7 +411,7 @@ class WebhooksCustomServerTest extends Scope 'name' => $name, ]); - $this->assertEquals($user['headers']['status-code'], 201); + $this->assertEquals(201, $user['headers']['status-code']); $this->assertNotEmpty($user['body']['$id']); $id = $user['body']['$id']; @@ -232,9 +419,9 @@ class WebhooksCustomServerTest extends Scope $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('users.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -245,11 +432,11 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); $this->assertNotEmpty($webhook['data']['$id']); $this->assertEquals($webhook['data']['name'], $name); - $this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['registration'])); - $this->assertEquals($webhook['data']['status'], true); + $this->assertTrue((new DatetimeValidator())->isValid($webhook['data']['registration'])); + $this->assertTrue($webhook['data']['status']); $this->assertEquals($webhook['data']['email'], $email); - $this->assertEquals($webhook['data']['emailVerification'], false); - $this->assertEquals($webhook['data']['prefs'], []); + $this->assertFalse($webhook['data']['emailVerification']); + $this->assertEquals([], $webhook['data']['prefs']); /** * Test for FAILURE @@ -274,15 +461,15 @@ class WebhooksCustomServerTest extends Scope 'prefs' => ['a' => 'b'] ]); - $this->assertEquals($user['headers']['status-code'], 200); - $this->assertEquals($user['body']['a'], 'b'); + $this->assertEquals(200, $user['headers']['status-code']); + $this->assertEquals('b', $user['body']['a']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('users.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('users.*.update.prefs', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -293,7 +480,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); - $this->assertEquals($webhook['data']['a'], 'b'); + $this->assertEquals('b', $webhook['data']['a']); return $data; } @@ -315,15 +502,15 @@ class WebhooksCustomServerTest extends Scope 'status' => false, ]); - $this->assertEquals($user['headers']['status-code'], 200); + $this->assertEquals(200, $user['headers']['status-code']); $this->assertNotEmpty($user['body']['$id']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('users.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('users.*.update.status', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -336,11 +523,11 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); $this->assertNotEmpty($webhook['data']['$id']); $this->assertEquals($webhook['data']['name'], $data['name']); - $this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['registration'])); - $this->assertEquals($webhook['data']['status'], false); + $this->assertTrue((new DatetimeValidator())->isValid($webhook['data']['registration'])); + $this->assertFalse($webhook['data']['status']); $this->assertEquals($webhook['data']['email'], $data['email']); - $this->assertEquals($webhook['data']['emailVerification'], false); - $this->assertEquals($webhook['data']['prefs']['a'], 'b'); + $this->assertFalse($webhook['data']['emailVerification']); + $this->assertEquals('b', $webhook['data']['prefs']['a']); return $data; } @@ -360,14 +547,14 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals($user['headers']['status-code'], 204); + $this->assertEquals(204, $user['headers']['status-code']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('users.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -378,11 +565,11 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); $this->assertNotEmpty($webhook['data']['$id']); $this->assertEquals($webhook['data']['name'], $data['name']); - $this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['registration'])); - $this->assertEquals($webhook['data']['status'], false); + $this->assertTrue((new DatetimeValidator())->isValid($webhook['data']['registration'])); + $this->assertFalse($webhook['data']['status']); $this->assertEquals($webhook['data']['email'], $data['email']); - $this->assertEquals($webhook['data']['emailVerification'], false); - $this->assertEquals($webhook['data']['prefs']['a'], 'b'); + $this->assertFalse($webhook['data']['emailVerification']); + $this->assertEquals('b', $webhook['data']['prefs']['a']); return $data; } @@ -406,15 +593,15 @@ class WebhooksCustomServerTest extends Scope $id = $function['body']['$id'] ?? ''; - $this->assertEquals($function['headers']['status-code'], 201); + $this->assertEquals(201, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); @@ -447,7 +634,7 @@ class WebhooksCustomServerTest extends Scope ] ]); - $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($function['body']['$id'], $data['functionId']); // Create variable @@ -464,9 +651,9 @@ class WebhooksCustomServerTest extends Scope $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -504,15 +691,15 @@ class WebhooksCustomServerTest extends Scope $functionId = $data['functionId'] ?? ''; $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals($deployment['headers']['status-code'], 202); + $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -544,7 +731,7 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); - $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']['$id']); // Wait for deployment to be built. @@ -553,9 +740,9 @@ class WebhooksCustomServerTest extends Scope $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.deployments.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -596,14 +783,14 @@ class WebhooksCustomServerTest extends Scope $executionId = $execution['body']['$id'] ?? ''; - $this->assertEquals($execution['headers']['status-code'], 202); + $this->assertEquals(202, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.executions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.executions.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -624,9 +811,9 @@ class WebhooksCustomServerTest extends Scope $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.executions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.executions.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -663,15 +850,15 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals($deployment['headers']['status-code'], 204); + $this->assertEquals(204, $deployment['headers']['status-code']); $this->assertEmpty($deployment['body']); $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.deployments.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); @@ -714,9 +901,9 @@ class WebhooksCustomServerTest extends Scope $webhook = $this->getLastRequest(); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals('POST', $webhook['method']); + $this->assertEquals('application/json', $webhook['headers']['Content-Type']); + $this->assertEquals('Appwrite-Server vdev. Please report abuse at security@appwrite.io', $webhook['headers']['User-Agent']); // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString('functions.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']); From 6bdfe3b8955dfac773fab926619c32fe9ef1654b Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:34:20 +0530 Subject: [PATCH 103/173] address naming comments. --- .../Tables/DatabasesConsoleClientTest.php | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php index 087ece167f..8a8d8de170 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php @@ -16,7 +16,7 @@ class DatabasesConsoleClientTest extends Scope use ProjectCustom; use SideConsole; - public function testCreateCollection(): array + public function testCreateTable(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -50,10 +50,10 @@ class DatabasesConsoleClientTest extends Scope ]); $this->assertEquals(201, $movies['headers']['status-code']); - $this->assertEquals($movies['body']['name'], 'Movies'); + $this->assertEquals('Movies', $movies['body']['name']); /** - * Test when database is disabled but can still create collections + * Test when database is disabled but can still create tables */ $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId, array_merge([ 'content-type' => 'application/json', @@ -81,7 +81,7 @@ class DatabasesConsoleClientTest extends Scope ]); /** - * Test when collection is disabled but can still modify collections + * Test when table is disabled but can still modify tables */ $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $movies['body']['$id'], array_merge([ 'content-type' => 'application/json', @@ -92,20 +92,20 @@ class DatabasesConsoleClientTest extends Scope ]); $this->assertEquals(201, $tvShows['headers']['status-code']); - $this->assertEquals($tvShows['body']['name'], 'TvShows'); + $this->assertEquals('TvShows', $tvShows['body']['name']); return ['moviesId' => $movies['body']['$id'], 'databaseId' => $databaseId, 'tvShowsId' => $tvShows['body']['$id']]; } /** - * @depends testCreateCollection + * @depends testCreateTable * @param array $data * @throws \Exception */ - public function testListCollection(array $data) + public function testListTable(array $data) { /** - * Test when database is disabled but can still call list collections + * Test when database is disabled but can still call list tables */ $databaseId = $data['databaseId']; @@ -119,17 +119,17 @@ class DatabasesConsoleClientTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @param array $data * @throws \Exception */ - public function testGetCollection(array $data) + public function testGetTable(array $data) { $databaseId = $data['databaseId']; $moviesCollectionId = $data['moviesId']; /** - * Test when database and collection are disabled but can still call get collection + * Test when database and table are disabled but can still call get table */ $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $moviesCollectionId, array_merge([ 'content-type' => 'application/json', @@ -143,18 +143,18 @@ class DatabasesConsoleClientTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @param array $data * @throws \Exception * @throws \Exception */ - public function testUpdateCollection(array $data) + public function testUpdateTable(array $data) { $databaseId = $data['databaseId']; $moviesCollectionId = $data['moviesId']; /** - * Test When database and collection are disabled but can still call update collection + * Test When database and table are disabled but can still call update table */ $table = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $moviesCollectionId, array_merge([ 'content-type' => 'application/json', @@ -171,18 +171,18 @@ class DatabasesConsoleClientTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @param array $data * @throws \Exception * @throws \Exception */ - public function testDeleteCollection(array $data) + public function testDeleteTable(array $data) { $databaseId = $data['databaseId']; $tvShowsId = $data['tvShowsId']; /** - * Test when database and collection are disabled but can still call delete collection + * Test when database and table are disabled but can still call delete table */ $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tvShowsId, array_merge([ 'content-type' => 'application/json', @@ -190,11 +190,11 @@ class DatabasesConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(204, $response['headers']['status-code']); - $this->assertEquals($response['body'], ""); + $this->assertEquals("", $response['body']); } /** - * @depends testCreateCollection + * @depends testCreateTable */ public function testGetDatabaseUsage(array $data) { @@ -226,7 +226,7 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(15, count($response['body'])); + $this->assertCount(15, $response['body']); $this->assertEquals('24h', $response['body']['range']); $this->assertIsNumeric($response['body']['rowsTotal']); $this->assertIsNumeric($response['body']['tablesTotal']); @@ -236,9 +236,9 @@ class DatabasesConsoleClientTest extends Scope /** - * @depends testCreateCollection + * @depends testCreateTable */ - public function testGetCollectionUsage(array $data) + public function testGetTableUsage(array $data) { $databaseId = $data['databaseId']; /** @@ -280,10 +280,10 @@ class DatabasesConsoleClientTest extends Scope } /** - * @depends testCreateCollection + * @depends testCreateTable * @throws \Utopia\Database\Exception\Query */ - public function testGetCollectionLogs(array $data) + public function testGetTableLogs(array $data) { $databaseId = $data['databaseId']; /** From 15e0385723f7e7d3a11dd07065cf7ae181cb7435 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:36:13 +0530 Subject: [PATCH 104/173] update: exception name. --- .../Databases/Http/Databases/Collections/Indexes/Action.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Create.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Get.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/XList.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index c1e3f61114..07a32ce122 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -86,7 +86,7 @@ abstract class Action extends UtopiaAction /** * Get the appropriate grandparent level not found exception. */ - final protected function getGrantParentNotFoundException(): string + final protected function getGrandParentNotFoundException(): string { return $this->isCollectionsAPI() ? Exception::COLLECTION_NOT_FOUND diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index c18ab69b10..a963640597 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -89,7 +89,7 @@ class Create extends Action if ($collection->isEmpty()) { // table or collection. - throw new Exception($this->getGrantParentNotFoundException()); + throw new Exception($this->getGrandParentNotFoundException()); } $count = $dbForProject->count('indexes', [ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index a208e059f6..70df7c4f7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -82,7 +82,7 @@ class Delete extends Action if ($collection->isEmpty()) { // table or collection. - throw new Exception($this->getGrantParentNotFoundException()); + throw new Exception($this->getGrandParentNotFoundException()); } $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index fa10406f2a..466d6fa99e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -71,7 +71,7 @@ class Get extends Action if ($collection->isEmpty()) { // table or collection. - throw new Exception($this->getGrantParentNotFoundException()); + throw new Exception($this->getGrandParentNotFoundException()); } $index = $collection->find('key', $key, 'indexes'); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index 733e6854b2..d44c80b393 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -77,7 +77,7 @@ class XList extends Action if ($collection->isEmpty()) { // table or collection. - throw new Exception($this->getGrantParentNotFoundException()); + throw new Exception($this->getGrandParentNotFoundException()); } $queries = Query::parseQueries($queries); From 9ba29d47799f10902e5ae8074fcfbdfd26d6aecb Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:36:55 +0530 Subject: [PATCH 105/173] update: var name. --- tests/e2e/Services/GraphQL/Base.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 8cad67b909..aa90502163 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -25,8 +25,8 @@ trait Base public static string $CREATE_TABLE = 'create_table'; public static string $GET_TABLE = 'get_table'; public static string $GET_TABLES = 'list_tables'; - public static string $UPDATE_TABLE = 'update_tables'; - public static string $DELETE_TABLE = 'delete_tables'; + public static string $UPDATE_TABLE = 'update_table'; + public static string $DELETE_TABLE = 'delete_table'; // Attributes public static string $CREATE_STRING_ATTRIBUTE = 'create_string_attribute'; From e50d7df56ca8616c3d464b59a3045c70e316d28d Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:38:28 +0530 Subject: [PATCH 106/173] fix: audits event. --- .../Databases/Http/Databases/Collections/Documents/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index bd59c9ee35..d6c9960a5b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -51,7 +51,7 @@ class Create extends Action ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'row.create') + ->label('audits.event', 'document.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) From 63d591e7664ee8ba0921ae3b46c72a69d4f3d37a Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:39:46 +0530 Subject: [PATCH 107/173] fix: method name. --- tests/e2e/Services/GraphQL/Tables/AbuseTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php index 80bde907f9..623d4ff783 100644 --- a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php @@ -29,7 +29,7 @@ class AbuseTest extends Scope public function testRateLimitEnforced() { - $data = $this->createCollection(); + $data = $this->createTable(); $databaseId = $data['databaseId']; $tableId = $data['tableId']; $projectId = $this->getProject()['$id']; @@ -114,7 +114,7 @@ class AbuseTest extends Scope $this->assertEquals('Too many queries.', $response['body']['message']); } - private function createCollection(): array + private function createTable(): array { $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::$CREATE_DATABASE); From b0d9215ea5ba68e1b20bf28b59950dfc16fb3393 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 11:57:32 +0530 Subject: [PATCH 108/173] fix: abuse tests! --- tests/e2e/Services/GraphQL/Base.php | 256 +++++++++++++++++- .../GraphQL/Collections/AbuseTest.php | 2 +- .../e2e/Services/GraphQL/Tables/AbuseTest.php | 8 +- 3 files changed, 255 insertions(+), 11 deletions(-) diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index aa90502163..f050fa9638 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -306,7 +306,8 @@ trait Base public static string $UPDATE_PUSH_NOTIFICATION = 'update_push_notification'; // Complex queries - public static string $COMPLEX_QUERY = 'complex_query'; + public static string $COMPLEX_QUERY_TABLE = 'complex_query_table'; + public static string $COMPLEX_QUERY_COLLECTION = 'complex_query_collection'; // Attribute Fragments public static string $FRAGMENT_ATTRIBUTES = ' @@ -2689,7 +2690,7 @@ trait Base status } }'; - case self::$COMPLEX_QUERY: + case self::$COMPLEX_QUERY_COLLECTION: return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) { databasesCreate(databaseId: $databaseId, name: $databaseName) { _id @@ -2700,7 +2701,7 @@ trait Base _createdAt _updatedAt _permissions - _databaseId + databaseId name documentSecurity attributes { @@ -2712,7 +2713,7 @@ trait Base status } } - databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "name", size: 255, required: true) { + collectionsCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "name", size: 255, required: true) { key type status @@ -2721,7 +2722,7 @@ trait Base default array } - databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "age", min: 0, max: 150, required: true) { + collectionsCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "age", min: 0, max: 150, required: true) { key type status @@ -2731,7 +2732,7 @@ trait Base default array } - databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "alive", required: false, default: true) { + collectionsCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "alive", required: false, default: true) { key type status @@ -2932,6 +2933,249 @@ trait Base } } }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; + case self::$COMPLEX_QUERY_TABLE: + return 'mutation complex($databaseId: String!, $databaseName: String!, $tableId: String!, $tableName: String!, $rowSecurity: Boolean!, $tablePermissions: [String!]!) { + databasesCreate(databaseId: $databaseId, name: $databaseName) { + _id + name + } + databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $tableName, rowSecurity: $rowSecurity, permissions: $tablePermissions) { + _id + _createdAt + _updatedAt + _permissions + databaseId + name + rowSecurity + columns { + ...columnProperties + } + indexes { + key + type + status + } + } + tablesCreateStringColumn(databaseId: $databaseId, tableId: $tableId, key: "name", size: 255, required: true) { + key + type + status + size + required + default + array + } + tablesCreateIntegerColumn(databaseId: $databaseId, tableId: $tableId, key: "age", min: 0, max: 150, required: true) { + key + type + status + required + min + max + default + array + } + tablesCreateBooleanColumn(databaseId: $databaseId, tableId: $tableId, key: "alive", required: false, default: true) { + key + type + status + required + default + array + } + user1: usersCreate(userId: "unique()", email: "test1@appwrite.io", password: "password", name: "Tester 1") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user2: usersCreate(userId: "unique()", email: "test2@appwrite.io", password: "password", name: "Tester 2") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user3: usersCreate(userId: "unique()", email: "test3@appwrite.io", password: "password", name: "Tester 3") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user4: usersCreate(userId: "unique()", email: "test4@appwrite.io", password: "password", name: "Tester 4") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user5: usersCreate(userId: "unique()", email: "test5@appwrite.io", password: "password", name: "Tester 5") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user6: usersCreate(userId: "unique()", email: "test6@appwrite.io", password: "password", name: "Tester 6") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user7: usersCreate(userId: "unique()", email: "test7@appwrite.io", password: "password", name: "Tester 7") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user8: usersCreate(userId: "unique()", email: "test8@appwrite.io", password: "password", name: "Tester 8") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user9: usersCreate(userId: "unique()", email: "test9@appwrite.io", password: "password", name: "Tester 9") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user10: usersCreate(userId: "unique()", email: "test10@appwrite.io", password: "password", name: "Tester 10") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user11: usersCreate(userId: "unique()", email: "test11@appwrite.io", password: "password", name: "Tester 11") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + user12: usersCreate(userId: "unique()", email: "test12@appwrite.io", password: "password", name: "Tester 5") { + _id + _createdAt + _updatedAt + name + phone + email + status + registration + passwordUpdate + emailVerification + phoneVerification + prefs { + data + } + } + }' . PHP_EOL . self::$FRAGMENT_COLUMNS; } throw new \InvalidArgumentException('Invalid query type'); diff --git a/tests/e2e/Services/GraphQL/Collections/AbuseTest.php b/tests/e2e/Services/GraphQL/Collections/AbuseTest.php index a45bebd714..a58a57ec5a 100644 --- a/tests/e2e/Services/GraphQL/Collections/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Collections/AbuseTest.php @@ -65,7 +65,7 @@ class AbuseTest extends Scope public function testComplexQueryBlocked() { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$COMPLEX_QUERY); + $query = $this->getQuery(self::$COMPLEX_QUERY_COLLECTION); $graphQLPayload = [ 'query' => $query, 'variables' => [ diff --git a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php index 623d4ff783..abb5019d55 100644 --- a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php @@ -65,7 +65,7 @@ class AbuseTest extends Scope public function testComplexQueryBlocked() { $projectId = $this->getProject()['$id']; - $query = $this->getQuery(self::$COMPLEX_QUERY); + $query = $this->getQuery(self::$COMPLEX_QUERY_TABLE); $graphQLPayload = [ 'query' => $query, 'variables' => [ @@ -75,8 +75,8 @@ class AbuseTest extends Scope 'databaseId' => 'database', 'databaseName' => 'database', 'tableId' => 'table', - 'collectionName' => 'table', - 'collectionPermissions' => [ + 'tableName' => 'table', + 'tablePermissions' => [ Permission::read(Role::users()), Permission::create(Role::users()), Permission::update(Role::users()), @@ -157,7 +157,7 @@ class AbuseTest extends Scope $tableId = $response['body']['data']['databasesCreateTable']['_id']; - $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ From 087ccfe347c03e7148c80045dc527e48e112c9bc Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 10 May 2025 12:06:17 +0530 Subject: [PATCH 109/173] remove: todo. --- app/config/scopes.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/config/scopes.php b/app/config/scopes.php index d459ffb2fc..7dea7b1cd5 100644 --- a/app/config/scopes.php +++ b/app/config/scopes.php @@ -1,6 +1,5 @@ [ 'description' => 'Access to create, update, and delete user sessions', From fcdddf7bf0511e696b3906012d72916a34a1c616 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 12:21:48 +0530 Subject: [PATCH 110/173] address comments. --- app/config/errors.php | 13 +++-- app/config/events.php | 2 +- app/init/constants.php | 12 ----- src/Appwrite/Event/Event.php | 48 ++++++++++++++++- src/Appwrite/Event/Realtime.php | 51 ++----------------- src/Appwrite/Extend/Exception.php | 45 ++++++++-------- src/Appwrite/Messaging/Adapter/Realtime.php | 2 - .../Platform/Modules/Databases/Constants.php | 23 +++++++++ 8 files changed, 106 insertions(+), 90 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Constants.php diff --git a/app/config/errors.php b/app/config/errors.php index de26b077fc..f7e8f194ab 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -69,9 +69,14 @@ return [ 'description' => 'The request contains one or more invalid arguments. Please refer to the endpoint documentation.', 'code' => 400, ], - Exception::GENERAL_QUERY_LIMIT_EXCEEDED => [ - 'name' => Exception::GENERAL_QUERY_LIMIT_EXCEEDED, - 'description' => 'Query limit exceeded for the current attribute/column. Usage of more than 100 query values on a single attribute/column is prohibited.', + Exception::GENERAL_ATTRIBUTE_QUERY_LIMIT_EXCEEDED => [ + 'name' => Exception::GENERAL_ATTRIBUTE_QUERY_LIMIT_EXCEEDED, + 'description' => 'Query limit exceeded for the current attribute.', + 'code' => 400, + ], + Exception::GENERAL_COLUMN_QUERY_LIMIT_EXCEEDED => [ + 'name' => Exception::GENERAL_COLUMN_QUERY_LIMIT_EXCEEDED, + 'description' => 'Query limit exceeded for the current column.', 'code' => 400, ], Exception::GENERAL_QUERY_INVALID => [ @@ -813,7 +818,7 @@ return [ ], Exception::ATTRIBUTE_INVALID_RESIZE => [ 'name' => Exception::ATTRIBUTE_INVALID_RESIZE, - 'description' => "Existing data is too large for new size, truncate your existing data then try again.", + 'description' => 'Existing data is too large for new size, truncate your existing data then try again.', 'code' => 400, ], diff --git a/app/config/events.php b/app/config/events.php index b0e6b9f5fc..8e759aaf56 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -129,7 +129,7 @@ return [ '$resource' => true, '$description' => 'This event triggers on any columns event.', 'create' => [ - '$description' => 'This event triggers when an column is created.', + '$description' => 'This event triggers when a column is created.', ], 'delete' => [ '$description' => 'This event triggers when an column is deleted.' diff --git a/app/init/constants.php b/app/init/constants.php index c3b6d4a058..143bba29bd 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -265,15 +265,3 @@ const TOKENS_RESOURCE_TYPE_FILES = 'files'; const TOKENS_RESOURCE_TYPE_SITES = 'sites'; const TOKENS_RESOURCE_TYPE_FUNCTIONS = 'functions'; const TOKENS_RESOURCE_TYPE_DATABASES = 'databases'; - -// Context constants for database - -const DATABASE_ROWS_CONTEXT = 'row'; -const DATABASE_TABLES_CONTEXT = 'table'; -const DATABASE_COLUMNS_CONTEXT = 'column'; -const DATABASE_INDEX_CONTEXT = 'index'; - -const DATABASE_DOCUMENTS_CONTEXT = 'document'; -const DATABASE_ATTRIBUTES_CONTEXT = 'attribute'; -const DATABASE_COLLECTIONS_CONTEXT = 'collection'; -const DATABASE_COLUMN_INDEX_CONTEXT = 'columnIndex'; diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index d699a45417..73ca8126a0 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -576,7 +576,12 @@ class Event /** * Force a non-assoc array. */ - return \array_values($events); + $eventValues = \array_values($events); + + /** + * Return a combined list of table, collection events. + */ + return Event::mirrorCollectionEvents($pattern, $eventValues[0], $eventValues); } /** @@ -597,4 +602,45 @@ class Event $this->context = $event->context; return $this; } + + /** + * Adds `table` events for `collection` events. + * + * Example: + * + * `databases.*.collections.*.documents.*.update` →\ + * `[databases.*.collections.*.documents.*.update, databases.*.tables.*.rows.*.update]` + */ + private static function mirrorCollectionEvents(string $pattern, string $firstEvent, array $events): array + { + $tableEventMap = [ + 'documents' => 'rows', + 'collections' => 'tables', + 'attributes' => 'columns', + ]; + + if ( + str_contains($pattern, 'databases.') && + str_contains($firstEvent, 'collections') + ) { + $pairedEvents = []; + + foreach ($events as $event) { + $pairedEvents[] = $event; + + if (str_contains($event, 'collections')) { + $tableSideEvent = str_replace( + array_keys($tableEventMap), + array_values($tableEventMap), + $event + ); + $pairedEvents[] = $tableSideEvent; + } + } + + $events = $pairedEvents; + } + + return $events; + } } diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index 016006ae73..6cb51ffa14 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -72,11 +72,7 @@ class Realtime extends Event return false; } - $events = Event::generateEvents($this->getEvent(), $this->getParams()); - $firstEvent = $events[0]; // most verbose event pattern - - // generate and merge all collection and tables api events. - $events = $this->mirrorCollectionEvents($firstEvent, $events); + $allEvents = Event::generateEvents($this->getEvent(), $this->getParams()); $payload = new Document($this->getPayload()); @@ -87,7 +83,7 @@ class Realtime extends Event $tableOrCollection = $this->getContext('table') ?? $this->getContext('collection'); $target = RealtimeAdapter::fromPayload( - event: $firstEvent, + event: $allEvents[0], payload: $payload, project: $this->getProject(), database: $db, @@ -103,7 +99,7 @@ class Realtime extends Event $this->realtime->send( projectId: $projectId, payload: $this->getRealtimePayload(), - events: $events, + events: $allEvents, channels: $target['channels'], roles: $target['roles'], options: [ @@ -115,45 +111,4 @@ class Realtime extends Event return true; } - - /** - * Adds `table` events for `collection` events. - * - * Example: - * - * `databases.*.collections.*.documents.*.update` →\ - * `[databases.*.collections.*.documents.*.update, databases.*.tables.*.rows.*.update]` - */ - private function mirrorCollectionEvents(string $firstEvent, array $events): array - { - $tableEventMap = [ - 'documents' => 'rows', - 'collections' => 'tables', - 'attributes' => 'columns', - ]; - - if ( - str_contains($this->getEvent(), 'databases.') && - str_contains($firstEvent, 'collections') - ) { - $pairedEvents = []; - - foreach ($events as $event) { - $pairedEvents[] = $event; - - if (str_contains($event, 'collections')) { - $tableSideEvent = str_replace( - array_keys($tableEventMap), - array_values($tableEventMap), - $event - ); - $pairedEvents[] = $tableSideEvent; - } - } - - $events = $pairedEvents; - } - - return $events; - } } diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 38a562571c..29880a03b1 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -48,7 +48,8 @@ class Exception extends \Exception public const GENERAL_SMTP_DISABLED = 'general_smtp_disabled'; public const GENERAL_PHONE_DISABLED = 'general_phone_disabled'; public const GENERAL_ARGUMENT_INVALID = 'general_argument_invalid'; - public const GENERAL_QUERY_LIMIT_EXCEEDED = 'general_query_limit_exceeded'; + public const GENERAL_COLUMN_QUERY_LIMIT_EXCEEDED = 'general_column_query_limit_exceeded'; + public const GENERAL_ATTRIBUTE_QUERY_LIMIT_EXCEEDED = 'general_attribute_query_limit_exceeded'; public const GENERAL_QUERY_INVALID = 'general_query_invalid'; public const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; public const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; @@ -197,9 +198,9 @@ class Exception extends \Exception public const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded'; /** Tables */ - public const TABLE_NOT_FOUND = 'table_not_found'; - public const TABLE_ALREADY_EXISTS = 'table_already_exists'; - public const TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; + public const TABLE_NOT_FOUND = 'table_not_found'; + public const TABLE_ALREADY_EXISTS = 'table_already_exists'; + public const TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; /** Documents */ public const DOCUMENT_NOT_FOUND = 'document_not_found'; @@ -211,13 +212,13 @@ class Exception extends \Exception public const DOCUMENT_DELETE_RESTRICTED = 'document_delete_restricted'; /** Rows */ - public const ROW_NOT_FOUND = 'row_not_found'; - public const ROW_INVALID_STRUCTURE = 'row_invalid_structure'; - public const ROW_MISSING_DATA = 'row_missing_data'; - public const ROW_MISSING_PAYLOAD = 'row_missing_payload'; - public const ROW_ALREADY_EXISTS = 'row_already_exists'; - public const ROW_UPDATE_CONFLICT = 'row_update_conflict'; - public const ROW_DELETE_RESTRICTED = 'row_delete_restricted'; + public const ROW_NOT_FOUND = 'row_not_found'; + public const ROW_INVALID_STRUCTURE = 'row_invalid_structure'; + public const ROW_MISSING_DATA = 'row_missing_data'; + public const ROW_MISSING_PAYLOAD = 'row_missing_payload'; + public const ROW_ALREADY_EXISTS = 'row_already_exists'; + public const ROW_UPDATE_CONFLICT = 'row_update_conflict'; + public const ROW_DELETE_RESTRICTED = 'row_delete_restricted'; /** Attributes */ public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found'; @@ -232,16 +233,16 @@ class Exception extends \Exception public const ATTRIBUTE_INVALID_RESIZE = 'attribute_invalid_resize'; /** Columns */ - public const COLUMN_NOT_FOUND = 'column_not_found'; - public const COLUMN_UNKNOWN = 'column_unknown'; - public const COLUMN_NOT_AVAILABLE = 'column_not_available'; - public const COLUMN_FORMAT_UNSUPPORTED = 'column_format_unsupported'; - public const COLUMN_DEFAULT_UNSUPPORTED = 'column_default_unsupported'; - public const COLUMN_ALREADY_EXISTS = 'column_already_exists'; - public const COLUMN_LIMIT_EXCEEDED = 'column_limit_exceeded'; - public const COLUMN_VALUE_INVALID = 'column_value_invalid'; - public const COLUMN_TYPE_INVALID = 'column_type_invalid'; - public const COLUMN_INVALID_RESIZE = 'column_invalid_resize'; + public const COLUMN_NOT_FOUND = 'column_not_found'; + public const COLUMN_UNKNOWN = 'column_unknown'; + public const COLUMN_NOT_AVAILABLE = 'column_not_available'; + public const COLUMN_FORMAT_UNSUPPORTED = 'column_format_unsupported'; + public const COLUMN_DEFAULT_UNSUPPORTED = 'column_default_unsupported'; + public const COLUMN_ALREADY_EXISTS = 'column_already_exists'; + public const COLUMN_LIMIT_EXCEEDED = 'column_limit_exceeded'; + public const COLUMN_VALUE_INVALID = 'column_value_invalid'; + public const COLUMN_TYPE_INVALID = 'column_type_invalid'; + public const COLUMN_INVALID_RESIZE = 'column_invalid_resize'; /** Relationship */ public const RELATIONSHIP_VALUE_INVALID = 'relationship_value_invalid'; @@ -347,7 +348,7 @@ class Exception extends \Exception public const MESSAGE_MISSING_SCHEDULE = 'message_missing_schedule'; /** Targets */ - public const TARGET_PROVIDER_INVALID_TYPE = 'target_provider_invalid_type'; + public const TARGET_PROVIDER_INVALID_TYPE = 'target_provider_invalid_type'; /** Schedules */ public const SCHEDULE_NOT_FOUND = 'schedule_not_found'; diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index bfe87c49db..aff4e400ce 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -312,12 +312,10 @@ class Realtime extends Adapter throw new \Exception('Collection or the Table needs to be passed to Realtime for Document/Row events in the Database.'); } - // 1.7.x - Tables API $channels[] = 'rows'; $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows'; $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows.' . $payload->getId(); - // 1.6.x - Collections API $channels[] = 'documents'; $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents'; $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId(); diff --git a/src/Appwrite/Platform/Modules/Databases/Constants.php b/src/Appwrite/Platform/Modules/Databases/Constants.php new file mode 100644 index 0000000000..b17248bf4d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Constants.php @@ -0,0 +1,23 @@ + Date: Wed, 11 Jun 2025 12:51:31 +0530 Subject: [PATCH 111/173] update: `getInternalId()` -> `getSequence()` --- .../Collections/Attributes/Action.php | 52 +++++++++---------- .../Collections/Attributes/Delete.php | 16 +++--- .../Databases/Collections/Attributes/Get.php | 4 +- .../Attributes/Relationship/Create.php | 8 +-- .../Collections/Attributes/XList.php | 10 ++-- .../Http/Databases/Collections/Create.php | 6 +-- .../Http/Databases/Collections/Delete.php | 6 +-- .../Collections/Documents/Create.php | 12 ++--- .../Collections/Documents/Delete.php | 10 ++-- .../Databases/Collections/Documents/Get.php | 8 +-- .../Collections/Documents/Logs/XList.php | 4 +- .../Collections/Documents/Update.php | 16 +++--- .../Databases/Collections/Documents/XList.php | 12 ++--- .../Http/Databases/Collections/Get.php | 2 +- .../Databases/Collections/Indexes/Create.php | 14 ++--- .../Databases/Collections/Indexes/Delete.php | 6 +-- .../Databases/Collections/Indexes/Get.php | 2 +- .../Databases/Collections/Indexes/XList.php | 6 +-- .../Http/Databases/Collections/Logs/XList.php | 4 +- .../Http/Databases/Collections/Update.php | 6 +-- .../Http/Databases/Collections/Usage/Get.php | 6 +-- .../Http/Databases/Collections/XList.php | 6 +-- .../Databases/Http/Databases/Create.php | 2 +- .../Databases/Http/Databases/Delete.php | 2 +- .../Databases/Http/Databases/Usage/Get.php | 10 ++-- 25 files changed, 115 insertions(+), 115 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 095b679223..2e0e598695 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -287,7 +287,7 @@ abstract class Action extends UtopiaAction throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $db->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); @@ -310,7 +310,7 @@ abstract class Action extends UtopiaAction if ($type === Database::VAR_RELATIONSHIP) { $options['side'] = Database::RELATION_SIDE_PARENT; - $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? ''); + $relatedCollection = $dbForProject->getDocument('database_' . $db->getSequence(), $options['relatedCollection'] ?? ''); if ($relatedCollection->isEmpty()) { $parent = $this->isCollectionsAPI() ? 'collection' : 'table'; throw new Exception($this->getParentNotFoundException(), "The related $parent was not found."); @@ -319,11 +319,11 @@ abstract class Action extends UtopiaAction try { $attribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + '$id' => ID::custom($db->getSequence() . '_' . $collection->getSequence() . '_' . $key), 'key' => $key, - 'databaseInternalId' => $db->getInternalId(), + 'databaseInternalId' => $db->getSequence(), 'databaseId' => $db->getId(), - 'collectionInternalId' => $collection->getInternalId(), + 'collectionInternalId' => $collection->getSequence(), 'collectionId' => $collectionId, 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck @@ -345,13 +345,13 @@ abstract class Action extends UtopiaAction } catch (LimitException) { throw new Exception($this->getLimitException()); } catch (Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); throw $e; } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { $twoWayKey = $options['twoWayKey']; @@ -361,11 +361,11 @@ abstract class Action extends UtopiaAction try { $twoWayAttribute = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $twoWayKey), + '$id' => ID::custom($db->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $twoWayKey), 'key' => $twoWayKey, - 'databaseInternalId' => $db->getInternalId(), + 'databaseInternalId' => $db->getSequence(), 'databaseId' => $db->getId(), - 'collectionInternalId' => $relatedCollection->getInternalId(), + 'collectionInternalId' => $relatedCollection->getSequence(), 'collectionId' => $relatedCollection->getId(), 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck @@ -389,13 +389,13 @@ abstract class Action extends UtopiaAction $dbForProject->deleteDocument('attributes', $attribute->getId()); throw new Exception($this->getLimitException()); } catch (Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $relatedCollection->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $relatedCollection->getSequence()); throw $e; } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $relatedCollection->getId()); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $relatedCollection->getSequence()); } $queueForDatabase @@ -434,13 +434,13 @@ abstract class Action extends UtopiaAction throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $db->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } - $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $attribute = $dbForProject->getDocument('attributes', $db->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($attribute->isEmpty()) { throw new Exception($this->getNotFoundException()); @@ -466,7 +466,7 @@ abstract class Action extends UtopiaAction throw new Exception($this->getDefaultUnsupportedException(), 'Cannot set default value for array ' . $this->getContext() . 's'); } - $collectionId = 'database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId(); + $collectionId = 'database_' . $db->getSequence() . '_collection_' . $collection->getSequence(); $attribute ->setAttribute('default', $default) @@ -546,9 +546,9 @@ abstract class Action extends UtopiaAction } if ($primaryDocumentOptions['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']); + $relatedCollection = $dbForProject->getDocument('database_' . $db->getSequence(), $primaryDocumentOptions['relatedCollection']); - $relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']); + $relatedAttribute = $dbForProject->getDocument('attributes', $db->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $primaryDocumentOptions['twoWayKey']); if (!empty($newKey) && $newKey !== $key) { $options['twoWayKey'] = $newKey; @@ -556,9 +556,9 @@ abstract class Action extends UtopiaAction $relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options); $relatedAttribute->setAttribute('options', $relatedOptions); - $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute); + $dbForProject->updateDocument('attributes', $db->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute); - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $relatedCollection->getId()); } } else { try { @@ -586,7 +586,7 @@ abstract class Action extends UtopiaAction $originalUid = $attribute->getId(); $attribute - ->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey)) + ->setAttribute('$id', ID::custom($db->getSequence() . '_' . $collection->getSequence() . '_' . $newKey)) ->setAttribute('key', $newKey); $dbForProject->updateDocument('attributes', $originalUid, $attribute); @@ -608,10 +608,10 @@ abstract class Action extends UtopiaAction } } } else { - $attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); + $attribute = $dbForProject->updateDocument('attributes', $db->getSequence() . '_' . $collection->getSequence() . '_' . $key, $attribute); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collection->getId()); $queueForEvents ->setContext('database', $db) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index cf5b7766b3..1f31097caf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -76,12 +76,12 @@ class Delete extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $db->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } - $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $attribute = $dbForProject->getDocument('attributes', $db->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($attribute->isEmpty()) { throw new Exception($this->getNotFoundException()); } @@ -98,18 +98,18 @@ class Delete extends Action $attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { $options = $attribute->getAttribute('options'); if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']); + $relatedCollection = $dbForProject->getDocument('database_' . $db->getSequence(), $options['relatedCollection']); if ($relatedCollection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } - $relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedAttribute = $dbForProject->getDocument('attributes', $db->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $options['twoWayKey']); if ($relatedAttribute->isEmpty()) { throw new Exception($this->getNotFoundException()); } @@ -118,8 +118,8 @@ class Delete extends Action $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']); - $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $options['relatedCollection']); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $relatedCollection->getSequence()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index 96e40c750d..ba91af54a2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -76,12 +76,12 @@ class Get extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } - $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $attribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($attribute->isEmpty()) { throw new Exception($this->getNotFoundException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index 193e3eaa09..0bbb3faa67 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -93,14 +93,14 @@ class Create extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } - $relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId); - $relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId()); + $relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId); + $relatedCollection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $relatedCollectionDocument->getSequence()); if ($relatedCollection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index 7f2f23df62..dac7d94ab5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -69,7 +69,7 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } @@ -78,8 +78,8 @@ class XList extends Action \array_push( $queries, - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$collection->getInternalId()]) + Query::equal('databaseInternalId', [$database->getSequence()]), + Query::equal('collectionInternalId', [$collection->getSequence()]) ); $cursor = \array_filter( @@ -97,8 +97,8 @@ class XList extends Action $attributeId = $cursor->getValue(); $cursorDocument = Authorization::skip( fn () => $dbForProject->find('attributes', [ - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$collection->getInternalId()]), + Query::equal('databaseInternalId', [$database->getSequence()]), + Query::equal('collectionInternalId', [$collection->getSequence()]), Query::equal('key', [$attributeId]), Query::limit(1), ]) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index a65bf9c311..6b80901e05 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -89,9 +89,9 @@ class Create extends Action $permissions = Permission::aggregate($permissions) ?? []; try { - $collection = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ + $collection = $dbForProject->createDocument('database_' . $database->getSequence(), new Document([ '$id' => $collectionId, - 'databaseInternalId' => $database->getInternalId(), + 'databaseInternalId' => $database->getSequence(), 'databaseId' => $databaseId, '$permissions' => $permissions, 'documentSecurity' => $documentSecurity, @@ -100,7 +100,7 @@ class Create extends Action 'search' => \implode(' ', [$collectionId, $name]), ])); - $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), permissions: $permissions, documentSecurity: $documentSecurity); + $dbForProject->createCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), permissions: $permissions, documentSecurity: $documentSecurity); } catch (DuplicateException) { throw new Exception($this->getDuplicateException()); } catch (LimitException) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php index d838d16b94..67bddbc9ff 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php @@ -72,17 +72,17 @@ class Delete extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getNotFoundException()); } - if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $collectionId)) { + if (!$dbForProject->deleteDocument('database_' . $database->getSequence(), $collectionId)) { $type = $this->getContext(); throw new Exception(Exception::GENERAL_SERVER_ERROR, "Failed to remove $type from DB"); } - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_COLLECTION) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index d6c9960a5b..23883c9ff4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -107,7 +107,7 @@ class Create extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); @@ -199,7 +199,7 @@ class Create extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($relations as &$relation) { @@ -213,7 +213,7 @@ class Create extends Action } if ($relation instanceof Document) { $current = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) + fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId()) ); if ($current->isEmpty()) { @@ -244,7 +244,7 @@ class Create extends Action $checkPermissions($collection, $document, Database::PERMISSION_CREATE); try { - $document = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document); + $document = $dbForProject->createDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $document); } catch (StructureException $e) { throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } catch (DuplicateException) { @@ -275,7 +275,7 @@ class Create extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -290,7 +290,7 @@ class Create extends Action $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection $response->addHeader('X-Debug-Operations', $operations); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index c218e713bf..32af5b0127 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -88,14 +88,14 @@ class Delete extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); } // Read permission should not be required for delete - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($document->isEmpty()) { throw new Exception($this->getNotFoundException()); @@ -104,7 +104,7 @@ class Delete extends Action try { $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) { $dbForProject->deleteDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId ); }); @@ -134,7 +134,7 @@ class Delete extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -149,7 +149,7 @@ class Delete extends Action $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection $response->addHeader('X-Debug-Operations', 1); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index aa01eebcdf..f8fd749dc2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -79,7 +79,7 @@ class Get extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); @@ -87,7 +87,7 @@ class Get extends Action try { $queries = Query::parseQueries($queries); - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); + $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries); } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); } catch (QueryException $e) { @@ -133,7 +133,7 @@ class Get extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -148,7 +148,7 @@ class Get extends Action $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); $response->addHeader('X-Debug-Operations', $operations); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index bb24d41b30..1053c00ff9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -82,13 +82,13 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId); + $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId); if ($document->isEmpty()) { throw new Exception($this->getNotFoundException()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 17da8e552d..58408ae4ab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -101,7 +101,7 @@ class Update extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); @@ -109,7 +109,7 @@ class Update extends Action // Read permission should not be required for update /** @var Document $document */ - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($document->isEmpty()) { throw new Exception($this->getNotFoundException()); @@ -179,7 +179,7 @@ class Update extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($relations as &$relation) { @@ -194,7 +194,7 @@ class Update extends Action } if ($relation instanceof Document) { $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( - 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId() )); $relation->removeAttribute('$collectionId'); @@ -202,7 +202,7 @@ class Update extends Action // Attribute $collection is required for Utopia. $relation->setAttribute( '$collection', - 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId() + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence() ); if ($oldDocument->isEmpty()) { @@ -226,7 +226,7 @@ class Update extends Action $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); $response->addHeader('X-Debug-Operations', $operations); @@ -234,7 +234,7 @@ class Update extends Action $document = $dbForProject->withRequestTimestamp( $requestTimestamp, fn () => $dbForProject->updateDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $document->getId(), $newDocument ) @@ -271,7 +271,7 @@ class Update extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 57af51d908..d11128ed7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -79,7 +79,7 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); @@ -108,7 +108,7 @@ class XList extends Action $documentId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($cursorDocument->isEmpty()) { $type = ucfirst($this->getContext()); @@ -118,8 +118,8 @@ class XList extends Action $cursor->setValue($cursorDocument); } try { - $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); + $documents = $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries); + $total = $dbForProject->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; @@ -165,7 +165,7 @@ class XList extends Action $relatedCollectionId = $relationship->getAttribute('relatedCollection'); // todo: Use local cache for this getDocument - $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)); foreach ($relations as $index => $doc) { if ($doc instanceof Document) { @@ -191,7 +191,7 @@ class XList extends Action $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); $response->addHeader('X-Debug-Operations', $operations); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php index bf06e29e26..9ff9e3e442 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php @@ -66,7 +66,7 @@ class Get extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getNotFoundException()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index a963640597..e688088640 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -85,7 +85,7 @@ class Create extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $db->getSequence(), $collectionId); if ($collection->isEmpty()) { // table or collection. @@ -93,8 +93,8 @@ class Create extends Action } $count = $dbForProject->count('indexes', [ - Query::equal('collectionInternalId', [$collection->getInternalId()]), - Query::equal('databaseInternalId', [$db->getInternalId()]) + Query::equal('collectionInternalId', [$collection->getSequence()]), + Query::equal('databaseInternalId', [$db->getSequence()]) ], 61); $limit = $dbForProject->getLimitForIndexes(); @@ -173,12 +173,12 @@ class Create extends Action } $index = new Document([ - '$id' => ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + '$id' => ID::custom($db->getSequence() . '_' . $collection->getSequence() . '_' . $key), 'key' => $key, 'status' => 'processing', // processing, available, failed, deleting, stuck - 'databaseInternalId' => $db->getInternalId(), + 'databaseInternalId' => $db->getSequence(), 'databaseId' => $databaseId, - 'collectionInternalId' => $collection->getInternalId(), + 'collectionInternalId' => $collection->getSequence(), 'collectionId' => $collectionId, 'type' => $type, 'attributes' => $attributes, @@ -201,7 +201,7 @@ class Create extends Action throw new Exception($this->getDuplicateException()); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index 70df7c4f7a..6fe3e3f42c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -78,14 +78,14 @@ class Delete extends Action if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $db->getSequence(), $collectionId); if ($collection->isEmpty()) { // table or collection. throw new Exception($this->getGrandParentNotFoundException()); } - $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $index = $dbForProject->getDocument('indexes', $db->getSequence() . '_' . $collection->getSequence() . '_' . $key); if (empty($index->getId())) { throw new Exception($this->getNotFoundException()); @@ -96,7 +96,7 @@ class Delete extends Action $index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index 466d6fa99e..8d5513e1c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -67,7 +67,7 @@ class Get extends Action if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { // table or collection. diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index d44c80b393..5929d56a91 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -73,7 +73,7 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { // table or collection. @@ -104,8 +104,8 @@ class XList extends Action $indexId = $cursor->getValue(); $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ - Query::equal('collectionInternalId', [$collection->getInternalId()]), - Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('collectionInternalId', [$collection->getSequence()]), + Query::equal('databaseInternalId', [$database->getSequence()]), Query::equal('key', [$indexId]), Query::limit(1) ])); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 7785ec2890..6e9f86260c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -81,8 +81,8 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + $collectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $collectionDocument->getSequence()); if ($collection->isEmpty()) { throw new Exception($this->getNotFoundException()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php index d7a2751166..e1e8da7017 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php @@ -78,7 +78,7 @@ class Update extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception($this->getNotFoundException()); } @@ -91,7 +91,7 @@ class Update extends Action $enabled ??= $collection->getAttribute('enabled', true); $collection = $dbForProject->updateDocument( - 'database_' . $database->getInternalId(), + 'database_' . $database->getSequence(), $collectionId, $collection ->setAttribute('name', $name) @@ -101,7 +101,7 @@ class Update extends Action ->setAttribute('search', \implode(' ', [$collectionId, $name])) ); - $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity); + $dbForProject->updateCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $permissions, $documentSecurity); $queueForEvents ->setContext('database', $database) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php index 995200fcc7..5d49be7723 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php @@ -67,8 +67,8 @@ class Get extends Action public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject): void { $database = $dbForProject->getDocument('databases', $databaseId); - $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + $collectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $collectionDocument->getSequence()); if ($collection->isEmpty()) { throw new Exception($this->getNotFoundException()); @@ -78,7 +78,7 @@ class Get extends Action $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), + str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collectionDocument->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index 1b50c3f090..e9df7db598 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -94,7 +94,7 @@ class XList extends Action } $collectionIdId = $cursor->getValue(); - $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionIdId); + $cursorDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionIdId); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, ucfirst($this->getContext()) . " '$collectionIdId' for the 'cursor' value not found."); @@ -106,8 +106,8 @@ class XList extends Action $filterQueries = Query::groupByType($queries)['filters']; try { - $collections = $dbForProject->find('database_' . $database->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); + $collections = $dbForProject->find('database_' . $database->getSequence(), $queries); + $total = $dbForProject->count('database_' . $database->getSequence(), $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php index a734cbdd22..1f368ec77a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php @@ -109,7 +109,7 @@ class Create extends Action 'orders' => $index['orders'], ]); } - $dbForProject->createCollection('database_' . $database->getInternalId(), $attributes, $indexes); + $dbForProject->createCollection('database_' . $database->getSequence(), $attributes, $indexes); } catch (DuplicateException) { throw new Exception(Exception::DATABASE_ALREADY_EXISTS); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php index e0651b5e5b..60df83bb77 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php @@ -73,7 +73,7 @@ class Delete extends Action } $dbForProject->purgeCachedDocument('databases', $database->getId()); - $dbForProject->purgeCachedCollection('databases_' . $database->getInternalId()); + $dbForProject->purgeCachedCollection('databases_' . $database->getSequence()); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_DATABASE) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index 222201d5ed..fab12a9735 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -70,11 +70,11 @@ class Get extends Action $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_READS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_WRITES) + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_COLLECTIONS), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_DOCUMENTS), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_STORAGE), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASES_OPERATIONS_READS), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASES_OPERATIONS_WRITES) ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { From e07a1817ab57d9175f9be29d0785fd3429b29ff5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 13:42:55 +0530 Subject: [PATCH 112/173] update: remove conflict, restrict and relationship exceptions from global handler. --- app/controllers/general.php | 19 ---------------- .../Collections/Attributes/Action.php | 22 +++++++++++++++++-- .../Collections/Documents/Action.php | 19 ++++++++++++++++ .../Collections/Documents/Delete.php | 9 +++++--- .../Collections/Documents/Update.php | 12 +++++----- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 46bc367d63..5aed7c21ea 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1174,14 +1174,6 @@ App::error() break; } break; - case 'Utopia\Database\Exception\Conflict': - $error = new AppwriteException( - $isTablesAPI - ? AppwriteException::ROW_UPDATE_CONFLICT - : AppwriteException::DOCUMENT_UPDATE_CONFLICT, - previous: $error - ); - break; case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; @@ -1203,20 +1195,9 @@ App::error() ? AppwriteException::ROW_ALREADY_EXISTS : AppwriteException::DOCUMENT_ALREADY_EXISTS ); - break; - case 'Utopia\Database\Exception\Restricted': - $error = new AppwriteException( - $isTablesAPI - ? AppwriteException::ROW_DELETE_RESTRICTED - : AppwriteException::DOCUMENT_DELETE_RESTRICTED - ); - break; case 'Utopia\Database\Exception\Authorization': $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); break; - case 'Utopia\Database\Exception\Relationship': - $error = new AppwriteException(AppwriteException::RELATIONSHIP_VALUE_INVALID, $error->getMessage(), previous: $error); - break; case 'Utopia\Database\Exception\NotFound': $error = new AppwriteException( $isTablesAPI diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 2e0e598695..92751bcbe4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -14,6 +14,8 @@ use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Index as IndexException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Relationship as RelationshipException; +use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Truncate as TruncateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; @@ -124,6 +126,16 @@ abstract class Action extends UtopiaAction : Exception::COLUMN_ALREADY_EXISTS; } + /** + * Get the correct invalid structure message. + */ + final protected function getInvalidStructureException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_INVALID_STRUCTURE + : Exception::ROW_INVALID_STRUCTURE; + } + /** * Get the appropriate limit exceeded exception. */ @@ -541,8 +553,14 @@ abstract class Action extends UtopiaAction newKey: $newKey, onDelete: $primaryDocumentOptions['onDelete'], ); - } catch (NotFoundException) { - throw new Exception($this->getNotFoundException()); + } catch (IndexException) { + throw new Exception(Exception::INDEX_INVALID); + } catch (LimitException) { + throw new Exception($this->getLimitException()); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } if ($primaryDocumentOptions['twoWay']) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 01ee2d9f84..32bb314cd2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -97,6 +97,25 @@ abstract class Action extends UtopiaAction : Exception::ROW_ALREADY_EXISTS; } + /** + * Get the appropriate conflict exception. + */ + final protected function getConflictException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_UPDATE_CONFLICT + : Exception::ROW_UPDATE_CONFLICT; + } + + /** + * Get the appropriate delete restricted exception. + */ + final protected function getRestrictedException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_DELETE_RESTRICTED + : Exception::ROW_DELETE_RESTRICTED; + } /** * Get the correct invalid structure message. diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 32af5b0127..9944de4e36 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -13,7 +13,8 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Conflict as ConflictException; +use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; @@ -108,8 +109,10 @@ class Delete extends Action $documentId ); }); - } catch (NotFoundException) { - throw new Exception($this->getParentNotFoundException()); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (RestrictedException) { + throw new Exception($this->getRestrictedException()); } // Add $collection and $databaseId for all documents diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 58408ae4ab..c3a2fe6df3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -13,9 +13,9 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization as AuthorizationException; +use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -239,14 +239,14 @@ class Update extends Action $newDocument ) ); - } catch (AuthorizationException) { - throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); } catch (DuplicateException) { throw new Exception($this->getDuplicateException()); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { throw new Exception($this->getInvalidStructureException(), $e->getMessage()); - } catch (NotFoundException) { - throw new Exception($this->getParentNotFoundException()); } // Add $collectionId and $databaseId for all documents From e6268fe5a64a3d8cd01d23b8af84bbbea4ad2055 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 15:58:22 +0530 Subject: [PATCH 113/173] update: remove query exceptions from global handler. --- app/controllers/general.php | 4 +-- .../Collections/Attributes/XList.php | 35 +++++++++++++------ .../Databases/Collections/Documents/Get.php | 10 +++--- .../Collections/Documents/Logs/XList.php | 3 -- .../Databases/Collections/Documents/XList.php | 2 ++ .../Databases/Collections/Indexes/XList.php | 9 ++++- .../Http/Databases/Collections/XList.php | 16 +++++---- .../Databases/Http/Databases/XList.php | 4 +++ 8 files changed, 56 insertions(+), 27 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 5aed7c21ea..9d9a03db92 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1177,9 +1177,6 @@ App::error() case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; - case 'Utopia\Database\Exception\Query': - $error = new AppwriteException(AppwriteException::GENERAL_QUERY_INVALID, $error->getMessage(), previous: $error); - break; case 'Utopia\Database\Exception\Structure': $error = new AppwriteException( $isTablesAPI @@ -1195,6 +1192,7 @@ App::error() ? AppwriteException::ROW_ALREADY_EXISTS : AppwriteException::DOCUMENT_ALREADY_EXISTS ); + // no break case 'Utopia\Database\Exception\Authorization': $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); break; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index dac7d94ab5..307cb98eff 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -11,6 +11,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; @@ -74,7 +75,11 @@ class XList extends Action throw new Exception($this->getParentNotFoundException()); } - $queries = Query::parseQueries($queries); + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } \array_push( $queries, @@ -95,33 +100,43 @@ class XList extends Action } $attributeId = $cursor->getValue(); - $cursorDocument = Authorization::skip( - fn () => $dbForProject->find('attributes', [ + try { + $cursorDocument = $dbForProject->findOne('attributes', [ Query::equal('databaseInternalId', [$database->getSequence()]), Query::equal('collectionInternalId', [$collection->getSequence()]), Query::equal('key', [$attributeId]), - Query::limit(1), - ]) - ); + ]); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } - if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { + if ($cursorDocument->isEmpty()) { $type = ucfirst($this->getContext()); throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "$type '$attributeId' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument[0]); + $cursor->setValue($cursorDocument); } - $filters = Query::groupByType($queries)['filters']; + $filterQueries = Query::groupByType($queries)['filters']; try { $attributes = $dbForProject->find('attributes', $queries); - $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); + $total = $dbForProject->count('attributes', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } catch (QueryException) { + throw new Exception(Exception::GENERAL_QUERY_INVALID); + } + + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') === Database::VAR_STRING) { + $filters = $attribute->getAttribute('filters', []); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + } } $response->dynamic(new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index f8fd749dc2..da730246bf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; @@ -71,10 +70,10 @@ class Get extends Action public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } @@ -87,9 +86,12 @@ class Get extends Action try { $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + try { $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries); - } catch (AuthorizationException) { - throw new Exception(Exception::USER_UNAUTHORIZED); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 1053c00ff9..2856d9ddd0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -77,19 +77,16 @@ class XList extends Action public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); - if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId); - if ($document->isEmpty()) { throw new Exception($this->getNotFoundException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index d11128ed7b..02e2900ac8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -125,6 +125,8 @@ class XList extends Action $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } $operations = 0; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index 5929d56a91..caf6c72559 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; @@ -80,7 +81,11 @@ class XList extends Action throw new Exception($this->getGrandParentNotFoundException()); } - $queries = Query::parseQueries($queries); + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } \array_push( $queries, @@ -126,6 +131,8 @@ class XList extends Action $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } $response->dynamic(new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index e9df7db598..15705e835e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; @@ -73,7 +74,11 @@ class XList extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $queries = Query::parseQueries($queries); + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } if (!empty($search)) { $queries[] = Query::search('search', $search); @@ -108,11 +113,10 @@ class XList extends Action try { $collections = $dbForProject->find('database_' . $database->getSequence(), $queries); $total = $dbForProject->count('database_' . $database->getSequence(), $filterQueries, APP_LIMIT_COUNT); - } catch (OrderException $e) { - $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; - $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; - $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } catch (OrderException) { + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL); + } catch (QueryException) { + throw new Exception(Exception::GENERAL_QUERY_INVALID); } $response->dynamic(new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php index 1fd0967b37..8bb9184baa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Query\Cursor; use Utopia\Platform\Action; @@ -98,7 +99,10 @@ class XList extends Action $total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); + } catch (QueryException) { + throw new Exception(Exception::GENERAL_QUERY_INVALID); } + $response->dynamic(new Document([ 'databases' => $databases, 'total' => $total, From 3a88c74109a1a1fea51dd8e3ea4b902d9259264e Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 16:18:06 +0530 Subject: [PATCH 114/173] update: remove structure exceptions from global handler. --- app/controllers/general.php | 9 --- .../Collections/Attributes/Action.php | 11 ++-- .../Collections/Documents/Update.php | 2 - .../Databases/Http/Databases/Create.php | 60 ++++++++++--------- 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 9d9a03db92..0d0a253661 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1177,15 +1177,6 @@ App::error() case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; - case 'Utopia\Database\Exception\Structure': - $error = new AppwriteException( - $isTablesAPI - ? AppwriteException::ROW_INVALID_STRUCTURE - : AppwriteException::DOCUMENT_INVALID_STRUCTURE, - $error->getMessage(), - previous: $error - ); - break; case 'Utopia\Database\Exception\Duplicate': $error = new AppwriteException( $isTablesAPI diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 92751bcbe4..3939a0370c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -395,17 +395,20 @@ abstract class Action extends UtopiaAction $dbForProject->checkAttribute($relatedCollection, $twoWayAttribute); $dbForProject->createDocument('attributes', $twoWayAttribute); } catch (DuplicateException) { - $dbForProject->deleteDocument('attributes', $attribute->getId()); throw new Exception($this->getDuplicateException()); } catch (LimitException) { - $dbForProject->deleteDocument('attributes', $attribute->getId()); throw new Exception($this->getLimitException()); + } catch (StructureException) { + throw new Exception($this->getInvalidStructureException()); } catch (Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $relatedCollection->getSequence()); + $dbForProject->deleteDocument('attributes', $attribute->getId()); throw $e; + } finally { + $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); } + // If operation succeeded, purge the cache for the related collection too $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $relatedCollection->getId()); $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $relatedCollection->getSequence()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index c3a2fe6df3..f69dfb3f98 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -228,8 +228,6 @@ class Update extends Action ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); - $response->addHeader('X-Debug-Operations', $operations); - try { $document = $dbForProject->withRequestTimestamp( $requestTimestamp, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php index 1f368ec77a..6e4dc5e136 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php @@ -14,6 +14,9 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Index as IndexException; +use Utopia\Database\Exception\Limit as LimitException; +use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -76,42 +79,41 @@ class Create extends Action 'enabled' => $enabled, 'search' => implode(' ', [$databaseId, $name]), ])); - $database = $dbForProject->getDocument('databases', $databaseId); + } catch (DuplicateException) { + throw new Exception(Exception::DATABASE_ALREADY_EXISTS); + } catch (StructureException $e) { + // TODO: @Jake, how do we handle this document/row? + // there's no context awareness at this level on what the api is. + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } - $collections = (Config::getParam('collections', [])['databases'] ?? [])['collections'] ?? []; - if (empty($collections)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.'); - } + $database = $dbForProject->getDocument('databases', $databaseId); - $attributes = []; - $indexes = []; + $collections = (Config::getParam('collections', [])['databases'] ?? [])['collections'] ?? []; + if (empty($collections)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.'); + } - foreach ($collections['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => $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'] ?? '' - ]); - } + $attributes = []; + foreach ($collections['attributes'] as $attribute) { + $attributes[] = new Document($attribute); + } - foreach ($collections['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => $index['$id'], - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } + $indexes = []; + foreach ($collections['indexes'] as $index) { + $indexes[] = new Document($index); + } + + try { $dbForProject->createCollection('database_' . $database->getSequence(), $attributes, $indexes); } catch (DuplicateException) { throw new Exception(Exception::DATABASE_ALREADY_EXISTS); + } catch (IndexException) { + throw new Exception(Exception::INDEX_INVALID); + } catch (LimitException) { + // TODO: @Jake, how do we handle this collection/table? + // there's no context awareness at this level on what the api is. + throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); } $queueForEvents->setParam('databaseId', $database->getId()); From 6b53337a23be7a29c8faafe248f1600a20d2ee04 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 16:44:32 +0530 Subject: [PATCH 115/173] update: remove duplicate exceptions from global handler. --- app/controllers/general.php | 7 ------- .../Http/Databases/Collections/Action.php | 10 ++++++++++ .../Collections/Attributes/Action.php | 19 +++++++++++-------- .../Http/Databases/Collections/Create.php | 19 +++++++++++++++++++ .../Collections/Documents/Create.php | 7 +++++-- 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 0d0a253661..6602198c0b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1177,13 +1177,6 @@ App::error() case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; - case 'Utopia\Database\Exception\Duplicate': - $error = new AppwriteException( - $isTablesAPI - ? AppwriteException::ROW_ALREADY_EXISTS - : AppwriteException::DOCUMENT_ALREADY_EXISTS - ); - // no break case 'Utopia\Database\Exception\Authorization': $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); break; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php index abec90f1a6..1028f4ea7f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php @@ -73,6 +73,16 @@ abstract class Action extends UtopiaAction : Exception::TABLE_ALREADY_EXISTS; } + /** + * Get the appropriate index invalid exception. + */ + final protected function getInvalidIndexException(): string + { + return $this->isCollectionsAPI() + ? Exception::INDEX_INVALID + : Exception::COLUMN_INDEX_INVALID; + } + /** * Get the exception to throw when the resource is not found. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 3939a0370c..088490ad02 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -13,7 +13,6 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Index as IndexException; use Utopia\Database\Exception\Limit as LimitException; -use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Truncate as TruncateException; @@ -592,14 +591,14 @@ abstract class Action extends UtopiaAction formatOptions: $options, newKey: $newKey ?? null ); - } catch (TruncateException) { - throw new Exception($this->getInvalidResizeException()); - } catch (NotFoundException) { - throw new Exception($this->getNotFoundException()); - } catch (LimitException) { - throw new Exception($this->getLimitException()); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); } catch (IndexException $e) { throw new Exception($this->getInvalidIndexException(), $e->getMessage()); + } catch (LimitException) { + throw new Exception($this->getLimitException()); + } catch (TruncateException) { + throw new Exception($this->getInvalidResizeException()); } } @@ -610,7 +609,11 @@ abstract class Action extends UtopiaAction ->setAttribute('$id', ID::custom($db->getSequence() . '_' . $collection->getSequence() . '_' . $newKey)) ->setAttribute('key', $newKey); - $dbForProject->updateDocument('attributes', $originalUid, $attribute); + try { + $dbForProject->updateDocument('attributes', $originalUid, $attribute); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } /** * @var Document $index diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index 6b80901e05..b5e7bf5415 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -13,7 +13,9 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Index as IndexException; use Utopia\Database\Exception\Limit as LimitException; +use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Authorization; @@ -86,6 +88,7 @@ class Create extends Action $collectionId = $collectionId === 'unique()' ? ID::unique() : $collectionId; + // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions) ?? []; try { @@ -105,6 +108,22 @@ class Create extends Action throw new Exception($this->getDuplicateException()); } catch (LimitException) { throw new Exception($this->getLimitException()); + } catch (NotFoundException) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + try { + $dbForProject->createCollection( + id: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + permissions: $permissions, + documentSecurity: $documentSecurity + ); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (IndexException) { + throw new Exception($this->getInvalidIndexException()); + } catch (LimitException) { + throw new Exception($this->getLimitException()); } $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 23883c9ff4..aef2ef4ace 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -245,12 +246,14 @@ class Create extends Action try { $document = $dbForProject->createDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $document); - } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } catch (DuplicateException) { throw new Exception($this->getDuplicateException()); } catch (NotFoundException) { throw new Exception($this->getParentNotFoundException()); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } // Add $collectionId and $databaseId for all documents From dc439d1a1670f9a61bcb24ae5106f282f167f744 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 16:46:21 +0530 Subject: [PATCH 116/173] update: remove authorization from global handler. --- app/controllers/general.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 6602198c0b..3a2a4c9d16 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1177,9 +1177,6 @@ App::error() case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; - case 'Utopia\Database\Exception\Authorization': - $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); - break; case 'Utopia\Database\Exception\NotFound': $error = new AppwriteException( $isTablesAPI From 86a9598a5154188d2c11e3afc7ed26fec804049b Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 16:51:09 +0530 Subject: [PATCH 117/173] update: remove not-found and index dependency exceptions from global handler. --- app/controllers/general.php | 18 ------------------ .../Collections/Attributes/Delete.php | 1 + 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 3a2a4c9d16..138e96f88a 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1177,24 +1177,6 @@ App::error() case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; - case 'Utopia\Database\Exception\NotFound': - $error = new AppwriteException( - $isTablesAPI - ? AppwriteException::TABLE_NOT_FOUND - : AppwriteException::COLLECTION_NOT_FOUND, - $error->getMessage(), - previous: $error - ); - break; - case 'Utopia\Database\Exception\Dependency': - $error = new AppwriteException( - $isTablesAPI - ? AppwriteException::COLUMN_INDEX_DEPENDENCY - : AppwriteException::INDEX_DEPENDENCY, - null, - previous: $error - ); - break; } $code = $error->getCode(); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 1f31097caf..242f6a4ea0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -90,6 +90,7 @@ class Delete extends Action $collection->getAttribute('indexes'), $dbForProject->getAdapter()->getSupportForCastIndexArray(), ); + if (!$validator->isValid($attribute)) { throw new Exception($this->getIndexDependencyException()); } From 3e1adc600409620c9bab9b03dc23a712aafdadbe Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 16:51:27 +0530 Subject: [PATCH 118/173] remove: `$isTablesAPI` var. --- app/controllers/general.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 138e96f88a..416e6b1e86 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1159,9 +1159,6 @@ App::error() Console::error('[Error] Line: ' . $line); } - // routes like /tables, /tables/:tableId, etc. - $isTablesAPI = str_contains($route->getPath(), '/databases/:databaseId/tables'); - switch ($class) { case 'Utopia\Exception': $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); From 8e2eff7894dabc8d55fd96c79c94adc0932d90d7 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 16:53:14 +0530 Subject: [PATCH 119/173] rollback: auth exception as per `1.8.x` source. --- app/controllers/general.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/controllers/general.php b/app/controllers/general.php index 416e6b1e86..0b64f30631 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1171,6 +1171,9 @@ App::error() break; } break; + case 'Utopia\Database\Exception\Authorization': + $error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED); + break; case 'Utopia\Database\Exception\Timeout': $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); break; From 7169e672459468c3ec5d3f9043cd6a7147f5c574 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 11 Jun 2025 20:05:09 +0530 Subject: [PATCH 120/173] add/update: `createDocuments` on module structure. --- .../Collections/Documents/Action.php | 25 +- .../Collections/Documents/Create.php | 269 +++++++++++++----- .../Http/Databases/Tables/Rows/Create.php | 38 ++- 3 files changed, 257 insertions(+), 75 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 32bb314cd2..c4e41d415f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -17,6 +17,11 @@ abstract class Action extends UtopiaAction */ abstract protected function getResponseModel(): string; + /** + * Get the response model used in the SDK and HTTP responses for bulk action. + */ + abstract protected function getBulkResponseModel(): string; + /** * Set the context to either `row` or `document`. * @@ -25,12 +30,22 @@ abstract class Action extends UtopiaAction final protected function setContext(string $context): void { if (!\in_array($context, [DATABASE_ROWS_CONTEXT, DATABASE_DOCUMENTS_CONTEXT], true)) { - throw new \InvalidArgumentException("Invalid context '{$context}'. Use `DATABASE_ROWS_CONTEXT` or `DATABASE_DOCUMENTS_CONTEXT`"); + throw new \InvalidArgumentException("Invalid context '$context'. Use `DATABASE_ROWS_CONTEXT` or `DATABASE_DOCUMENTS_CONTEXT`"); } $this->context = $context; } + /** + * Get the plural of the given name. + * + * Used for endpoints with multiple sdk methods. + */ + final protected function getBulkActionName(string $name): string + { + return "{$name}s"; + } + /** * Get the current context. */ @@ -67,6 +82,14 @@ abstract class Action extends UtopiaAction return $this->isCollectionsAPI() ? 'collections' : 'tables'; } + /** + * Get the correct attribute/column structure context for errors. + */ + final protected function getStructureContext(): string + { + return $this->isCollectionsAPI() ? 'attributes' : 'columns'; + } + /** * Get the appropriate parent level not found exception. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index aef2ef4ace..b55731a4f2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -9,6 +9,7 @@ use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; +use Appwrite\SDK\Parameter; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; @@ -26,6 +27,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; class Create extends Action @@ -42,6 +44,11 @@ class Create extends Action return UtopiaResponse::MODEL_DOCUMENT; } + protected function getBulkResponseModel(): string + { + return UtopiaResponse::MODEL_DOCUMENT_LIST; + } + public function __construct() { $this @@ -70,14 +77,41 @@ class Create extends Action model: $this->getResponseModel(), ) ], - contentType: ContentType::JSON + contentType: ContentType::JSON, + parameters: [ + new Parameter('databaseId', optional: false), + new Parameter('collectionId', optional: false), + new Parameter('documentId', optional: false), + new Parameter('data', optional: false), + new Parameter('permissions', optional: true), + ] + ), + new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: $this->getBulkActionName(self::getName()), + description: '/docs/references/databases/create-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getBulkResponseModel(), + ) + ], + contentType: ContentType::JSON, + parameters: [ + new Parameter('databaseId', optional: false), + new Parameter('collectionId', optional: false), + new Parameter('documents', optional: false), + ] ) ]) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') - ->param('data', [], new JSON(), 'Document data as JSON object.') + ->param('data', [], new JSON(), 'Document data as JSON object.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) ->inject('response') ->inject('dbForProject') ->inject('user') @@ -86,77 +120,128 @@ class Create extends Action ->callback([$this, 'action']); } - public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage): void { - $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + $data = \is_string($data) + ? \json_decode($data, true) + : $data; - if (empty($data)) { + /** + * Determine which internal path to call, single or bulk + */ + if (empty($data) && empty($documents)) { + // No single or bulk documents provided throw new Exception($this->getMissingDataException()); } - - if (isset($data['$id'])) { - // `rows` or `documents` in message. - throw new Exception($this->getInvalidStructureException(), '$id is not allowed for creating new ' . $this->getContext() . 's, try update instead'); + if (!empty($data) && !empty($documents)) { + // Both single and bulk documents provided + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'You can only send one of the following parameters: data, ' . $this->getSdkGroup()); + } + if (!empty($data) && empty($documentId)) { + // Single document provided without document ID + $document = $this->isCollectionsAPI() ? 'Document' : 'Row'; + $message = "$document ID is required when creating a single " . strtolower($document) . '.'; + throw new Exception($this->getMissingDataException(), $message); + } + if (!empty($documents) && !empty($documentId)) { + // Bulk documents provided with document ID + $documentId = $this->isCollectionsAPI() ? 'documentId' : 'rowId'; + throw new Exception( + Exception::GENERAL_BAD_REQUEST, + "Param \"$documentId\" is not allowed when creating multiple " . $this->getSdkGroup() . ', set "$id" on each instead.' + ); + } + if (!empty($documents) && !empty($permissions)) { + // Bulk documents provided with permissions + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Param "permissions" is disallowed when creating multiple ' . $this->getSdkGroup() . ', set "$permissions" on each instead'); } - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isBulk = true; + if (!empty($data)) { + // Single document provided, convert to single item array + // But remember that it was single to respond with a single document + $isBulk = false; + $documents = [$data]; + } $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + if ($isBulk && !$isAPIKey && !$isPrivilegedUser) { + throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE); + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception($this->getParentNotFoundException()); } - $allowedPermissions = [ - Database::PERMISSION_READ, - Database::PERMISSION_UPDATE, - Database::PERMISSION_DELETE, - ]; + $hasRelationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); - // Map aggregate permissions to into the set of individual permissions they represent. - $permissions = Permission::aggregate($permissions, $allowedPermissions); - - // Add permissions for current the user if none were provided. - if (\is_null($permissions)) { - $permissions = []; - if (!empty($user->getId())) { - foreach ($allowedPermissions as $permission) { - $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); - } - } + if ($isBulk && $hasRelationships) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for ' . $this->getSdkNamespace() .' with relationship ' . $this->getStructureContext()); } - // Users can only manage their own roles, API keys and Admin users can manage any - if (!$isAPIKey && !$isPrivilegedUser) { - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); + $setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk) { + $allowedPermissions = [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]; + + // If bulk, we need to validate permissions explicitly per document + if ($isBulk) { + $permissions = $document['$permissions'] ?? null; + if (!empty($permissions)) { + $validator = new Permissions(); + if (!$validator->isValid($permissions)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); } } } - } - $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers - $data['$id'] = $documentId == 'unique()' ? ID::unique() : $documentId; - $data['$permissions'] = $permissions; - $document = new Document($data); + $permissions = Permission::aggregate($permissions, $allowedPermissions); + + // Add permissions for current the user if none were provided. + if (\is_null($permissions)) { + $permissions = []; + if (!empty($user->getId())) { + foreach ($allowedPermissions as $permission) { + $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); + } + } + } + + // Users can only manage their own roles, API keys and Admin users can manage any + if (!$isAPIKey && !$isPrivilegedUser) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); + } + } + } + } + + $document->setAttribute('$permissions', $permissions); + }; $operations = 0; @@ -242,10 +327,36 @@ class Create extends Action } }; - $checkPermissions($collection, $document, Database::PERMISSION_CREATE); + $documents = \array_map(function ($document) use ($collection, $permissions, $checkPermissions, $isBulk, $documentId, $setPermissions) { + $document['$collection'] = $collection->getId(); + + // Determine the source ID depending on whether it's a bulk operation. + $sourceId = $isBulk + ? ($document['$id'] ?? ID::unique()) + : $documentId; + + // If bulk, we need to validate ID explicitly + if ($isBulk) { + $validator = new CustomId(); + if (!$validator->isValid($sourceId)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); + } + } + + // Assign a unique ID if needed, otherwise use the provided ID. + $document['$id'] = $sourceId === 'unique()' ? ID::unique() : $sourceId; + $document = new Document($document); + $setPermissions($document, $permissions); + $checkPermissions($collection, $document, Database::PERMISSION_CREATE); + + return $document; + }, $documents); try { - $document = $dbForProject->createDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $document); + $dbForProject->createDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documents + ); } catch (DuplicateException) { throw new Exception($this->getDuplicateException()); } catch (NotFoundException) { @@ -256,8 +367,22 @@ class Create extends Action throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setContext('collection', $collection) + ->setContext('database', $database); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) + ->setContext($this->getCollectionsEventsContext(), $collection); + // Add $collectionId and $databaseId for all documents $processDocument = function (Document $table, Document $document) use (&$processDocument, $dbForProject, $database) { + $document->removeAttribute('$collection'); $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $table->getId()); @@ -289,34 +414,34 @@ class Create extends Action } }; - $processDocument($collection, $document); + foreach ($documents as $document) { + $processDocument($collection, $document); + } $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) - ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); // per collection - $response->addHeader('X-Debug-Operations', $operations); + $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($document, $this->getResponseModel()); + if ($isBulk) { + $response->dynamic(new Document([ + 'total' => count($documents), + $this->getSdkGroup() => $documents + ]), $this->getBulkResponseModel()); - $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), - \array_filter( - $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); + return; + } $queueForEvents - ->setParam('databaseId', $databaseId) - ->setContext('database', $database) - ->setParam('collectionId', $collection->getId()) - ->setParam('tableId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setParam('rowId', $document->getId()) - ->setPayload($response->getPayload(), sensitive: $relationships) - ->setContext($this->getCollectionsEventsContext(), $collection); + ->setParam('documentId', $documents[0]->getId()) + ->setParam('rowId', $documents[0]->getId()) + // TODO: @itznotabug - check if the events mirroring works here! + ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); + + $response->dynamic( + $documents[0], + $this->getResponseModel() + ); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index d041630894..881e562c10 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -6,6 +6,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Cre use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; +use Appwrite\SDK\Parameter; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; @@ -14,6 +15,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; class Create extends DocumentCreate @@ -30,6 +32,11 @@ class Create extends DocumentCreate return UtopiaResponse::MODEL_ROW; } + protected function getBulkResponseModel(): string + { + return UtopiaResponse::MODEL_ROW_LIST; + } + public function __construct() { $this->setContext(DATABASE_ROWS_CONTEXT); @@ -57,10 +64,36 @@ class Create extends DocumentCreate responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, - model: self::getResponseModel(), + model: $this->getResponseModel(), ) ], - contentType: ContentType::JSON + contentType: ContentType::JSON, + parameters: [ + new Parameter('databaseId', optional: false), + new Parameter('tableId', optional: false), + new Parameter('rowId', optional: false), + new Parameter('data', optional: false), + new Parameter('permissions', optional: true), + ] + ), + new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: $this->getBulkActionName(self::getName()), + description: '/docs/references/databases/create-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getBulkResponseModel(), + ) + ], + contentType: ContentType::JSON, + parameters: [ + new Parameter('databaseId', optional: false), + new Parameter('tableId', optional: false), + new Parameter('rows', optional: false), + ] ) ]) ->param('databaseId', '', new UID(), 'Database ID.') @@ -68,6 +101,7 @@ class Create extends DocumentCreate ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.') ->param('data', [], new JSON(), 'Row data as JSON object.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) ->inject('response') ->inject('dbForProject') ->inject('user') From 7beae215352fcab2f8d1623cfde1ac833735b903 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 14:20:22 +0530 Subject: [PATCH 121/173] fix: missing context! --- .../Platform/Modules/Databases/Constants.php | 23 ---------------- .../Platform/Modules/Databases/Context.php | 26 +++++++++++++++++++ .../Http/Databases/Collections/Action.php | 11 ++++---- .../Collections/Attributes/Action.php | 9 ++++--- .../Collections/Documents/Action.php | 14 ++++------ .../Databases/Collections/Indexes/Action.php | 17 ++++++------ .../Tables/Columns/Boolean/Create.php | 3 ++- .../Tables/Columns/Boolean/Update.php | 3 ++- .../Tables/Columns/Datetime/Create.php | 3 ++- .../Tables/Columns/Datetime/Update.php | 3 ++- .../Http/Databases/Tables/Columns/Delete.php | 3 ++- .../Databases/Tables/Columns/Email/Create.php | 3 ++- .../Databases/Tables/Columns/Email/Update.php | 3 ++- .../Databases/Tables/Columns/Enum/Create.php | 3 ++- .../Databases/Tables/Columns/Enum/Update.php | 3 ++- .../Databases/Tables/Columns/Float/Create.php | 3 ++- .../Databases/Tables/Columns/Float/Update.php | 3 ++- .../Http/Databases/Tables/Columns/Get.php | 3 ++- .../Databases/Tables/Columns/IP/Create.php | 3 ++- .../Databases/Tables/Columns/IP/Update.php | 3 ++- .../Tables/Columns/Integer/Create.php | 3 ++- .../Tables/Columns/Integer/Update.php | 3 ++- .../Tables/Columns/Relationship/Create.php | 3 ++- .../Tables/Columns/Relationship/Update.php | 3 ++- .../Tables/Columns/String/Create.php | 3 ++- .../Tables/Columns/String/Update.php | 3 ++- .../Databases/Tables/Columns/URL/Create.php | 3 ++- .../Databases/Tables/Columns/URL/Update.php | 3 ++- .../Http/Databases/Tables/Columns/XList.php | 3 ++- .../Http/Databases/Tables/Create.php | 3 ++- .../Http/Databases/Tables/Delete.php | 3 ++- .../Databases/Http/Databases/Tables/Get.php | 3 ++- .../Http/Databases/Tables/Indexes/Create.php | 3 ++- .../Http/Databases/Tables/Indexes/Delete.php | 3 ++- .../Http/Databases/Tables/Indexes/Get.php | 3 ++- .../Http/Databases/Tables/Indexes/XList.php | 3 ++- .../Http/Databases/Tables/Logs/XList.php | 3 ++- .../Http/Databases/Tables/Rows/Create.php | 3 ++- .../Http/Databases/Tables/Rows/Delete.php | 3 ++- .../Http/Databases/Tables/Rows/Get.php | 3 ++- .../Http/Databases/Tables/Rows/Logs/XList.php | 3 ++- .../Http/Databases/Tables/Rows/Update.php | 3 ++- .../Http/Databases/Tables/Rows/XList.php | 3 ++- .../Http/Databases/Tables/Update.php | 3 ++- .../Http/Databases/Tables/Usage/Get.php | 3 ++- .../Databases/Http/Databases/Tables/XList.php | 3 ++- 46 files changed, 131 insertions(+), 89 deletions(-) delete mode 100644 src/Appwrite/Platform/Modules/Databases/Constants.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Context.php diff --git a/src/Appwrite/Platform/Modules/Databases/Constants.php b/src/Appwrite/Platform/Modules/Databases/Constants.php deleted file mode 100644 index b17248bf4d..0000000000 --- a/src/Appwrite/Platform/Modules/Databases/Constants.php +++ /dev/null @@ -1,23 +0,0 @@ -context = $context; @@ -52,7 +53,7 @@ abstract class Action extends UtopiaAction */ final protected function isCollectionsAPI(): bool { - return $this->getContext() === DATABASE_COLLECTIONS_CONTEXT; + return $this->getContext() === Context::DATABASE_COLLECTIONS; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 088490ad02..2ee265a18d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attribu use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response as UtopiaResponse; use Throwable; @@ -28,7 +29,7 @@ abstract class Action extends UtopiaAction /** * @var string|null The current context (either 'column' or 'attribute') */ - private ?string $context = DATABASE_ATTRIBUTES_CONTEXT; + private ?string $context = Context::DATABASE_ATTRIBUTES; /** * Get the correct response model. @@ -42,8 +43,8 @@ abstract class Action extends UtopiaAction */ final protected function setContext(string $context): void { - if (!\in_array($context, [DATABASE_COLUMNS_CONTEXT, DATABASE_ATTRIBUTES_CONTEXT], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Use `DATABASE_COLUMNS_CONTEXT` or `DATABASE_ATTRIBUTES_CONTEXT`"); + if (!\in_array($context, [Context::DATABASE_COLUMNS, Context::DATABASE_ATTRIBUTES], true)) { + throw new \InvalidArgumentException("Invalid context '$context'. Use `Context::DATABASE_COLUMNS` or `Context::DATABASE_ATTRIBUTES`"); } $this->context = $context; @@ -64,7 +65,7 @@ abstract class Action extends UtopiaAction { // columns in tables context // attributes in collections context - return $this->getContext() === DATABASE_ATTRIBUTES_CONTEXT; + return $this->getContext() === Context::DATABASE_ATTRIBUTES; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index c4e41d415f..341302f779 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Context; use Utopia\Platform\Action as UtopiaAction; abstract class Action extends UtopiaAction @@ -10,18 +11,13 @@ abstract class Action extends UtopiaAction /** * @var string|null The current context (either 'row' or 'document') */ - private ?string $context = DATABASE_DOCUMENTS_CONTEXT; + private ?string $context = Context::DATABASE_DOCUMENTS; /** * Get the response model used in the SDK and HTTP responses. */ abstract protected function getResponseModel(): string; - /** - * Get the response model used in the SDK and HTTP responses for bulk action. - */ - abstract protected function getBulkResponseModel(): string; - /** * Set the context to either `row` or `document`. * @@ -29,8 +25,8 @@ abstract class Action extends UtopiaAction */ final protected function setContext(string $context): void { - if (!\in_array($context, [DATABASE_ROWS_CONTEXT, DATABASE_DOCUMENTS_CONTEXT], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Use `DATABASE_ROWS_CONTEXT` or `DATABASE_DOCUMENTS_CONTEXT`"); + if (!\in_array($context, [Context::DATABASE_ROWS, Context::DATABASE_DOCUMENTS], true)) { + throw new \InvalidArgumentException("Invalid context '$context'. Use `Context::DATABASE_ROWS` or `Context::DATABASE_DOCUMENTS`"); } $this->context = $context; @@ -61,7 +57,7 @@ abstract class Action extends UtopiaAction { // rows in tables api context // documents in collections api context - return $this->getContext() === DATABASE_DOCUMENTS_CONTEXT; + return $this->getContext() === Context::DATABASE_DOCUMENTS; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index 07a32ce122..4a40ea6b5f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Databases\Context; use Utopia\Platform\Action as UtopiaAction; abstract class Action extends UtopiaAction @@ -10,7 +11,7 @@ abstract class Action extends UtopiaAction /** * The current API context (either 'columnIndex' or 'index'). */ - private ?string $context = DATABASE_INDEX_CONTEXT; + private ?string $context = Context::DATABASE_INDEX; /** * Get the response model used in the SDK and HTTP responses. @@ -20,12 +21,12 @@ abstract class Action extends UtopiaAction /** * Set the current API context. * - * @param string $context Must be either `DATABASE_INDEX_CONTEXT` or `DATABASE_COLUMN_INDEX_CONTEXT`. + * @param string $context Must be either `DATABASE_INDEX` or `DATABASE_COLUMN_INDEX`. */ final protected function setContext(string $context): void { - if (!\in_array($context, [DATABASE_INDEX_CONTEXT, DATABASE_COLUMN_INDEX_CONTEXT], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Must be either `DATABASE_COLUMN_INDEX_CONTEXT` or `DATABASE_INDEX_CONTEXT`."); + if (!\in_array($context, [Context::DATABASE_INDEX, Context::DATABASE_COLUMN_INDEX], true)) { + throw new \InvalidArgumentException("Invalid context '$context'. Must be either `Context::DATABASE_COLUMN_INDEX` or `Context::DATABASE_INDEX`."); } $this->context = $context; @@ -36,9 +37,9 @@ abstract class Action extends UtopiaAction */ final protected function getParentContext(): string { - return $this->getContext() === DATABASE_INDEX_CONTEXT - ? DATABASE_ATTRIBUTES_CONTEXT - : DATABASE_COLUMNS_CONTEXT; + return $this->getContext() === Context::DATABASE_INDEX + ? Context::DATABASE_ATTRIBUTES + : Context::DATABASE_COLUMNS; } /** @@ -54,7 +55,7 @@ abstract class Action extends UtopiaAction */ final protected function isCollectionsAPI(): bool { - return $this->getParentContext() === DATABASE_ATTRIBUTES_CONTEXT; + return $this->getParentContext() === Context::DATABASE_ATTRIBUTES; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php index ada1d83424..e1633da062 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Boolean; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as BooleanCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -29,7 +30,7 @@ class Create extends BooleanCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php index 5316677adc..deead5e1d7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Boolean; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as BooleanUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -31,7 +32,7 @@ class Update extends BooleanUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php index a0731f0767..4710031c63 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Datetime; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as DatetimeCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -31,7 +32,7 @@ class Create extends DatetimeCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php index 495e96f025..f30e627553 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Datetime; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Update as DatetimeUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -33,7 +34,7 @@ class Update extends DatetimeUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php index 68cb00e18b..d2f0c54839 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Delete as AttributesDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -30,7 +31,7 @@ class Delete extends AttributesDelete public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php index 569d641118..fc64c3b215 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Email; use Appwrite\Network\Validator\Email; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Email\Create as EmailCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -30,7 +31,7 @@ class Create extends EmailCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php index bb3abb10d3..c6425043b5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Email; use Appwrite\Network\Validator\Email; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Email\Update as EmailUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Update extends EmailUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php index 47d06d4a53..338cec4f05 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Enum; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Enum\Create as EnumCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -32,7 +33,7 @@ class Create extends EnumCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php index 18c1db0683..6a35b955d2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Enum; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Enum\Update as EnumUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -34,7 +35,7 @@ class Update extends EnumUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php index 217378e274..7e1a502f2e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Float; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Float\Create as FloatCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -30,7 +31,7 @@ class Create extends FloatCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php index be9e2f6b09..05983a8153 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Float; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Float\Update as FloatUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Update extends FloatUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php index 630ac01e0f..a0d8a3ce76 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Get as AttributesGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -39,7 +40,7 @@ class Get extends AttributesGet public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php index 4da949dd74..1bae06a360 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\IP; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Create as IPCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -30,7 +31,7 @@ class Create extends IPCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php index 7adda8a4d5..8174ad7860 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\IP; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Update as IPUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Update extends IPUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php index 2172b5067b..2c6b7a84e7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Integer; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Create as IntegerCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -30,7 +31,7 @@ class Create extends IntegerCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php index 26f7e8625c..8375853e83 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Integer; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Update as IntegerUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Update extends IntegerUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index 76dd80cec9..6d3f26902d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Relationship; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Create as RelationshipCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -31,7 +32,7 @@ class Create extends RelationshipCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php index b2229bb32d..8bed5013a9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Relationship; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Update as RelationshipUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -31,7 +32,7 @@ class Update extends RelationshipUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index cf12e6582d..529f4f270d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\String; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Create as StringCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -32,7 +33,7 @@ class Create extends StringCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php index 968b15bbef..7450af9eff 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\String; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Update as StringUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -34,7 +35,7 @@ class Update extends StringUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php index 8c9865cde0..07295b2d93 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\URL; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\URL\Create as URLCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -30,7 +31,7 @@ class Create extends URLCreate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php index e461befe0b..990fbee742 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\URL; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\URL\Update as URLUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Update extends URLUpdate public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index 3dde78441d..41b1aee5c9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XList as AttributesXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -28,7 +29,7 @@ class XList extends AttributesXList public function __construct() { - $this->setContext(DATABASE_COLUMNS_CONTEXT); + $this->setContext(Context::DATABASE_COLUMNS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index fcd52d4854..95ef8bea4a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CollectionCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Create extends CollectionCreate public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index c24fad28ef..bb9b859a78 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as CollectionDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -28,7 +29,7 @@ class Delete extends CollectionDelete public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index e277db6d3d..cdf7950a1c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Get as CollectionGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -28,7 +29,7 @@ class Get extends CollectionGet public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index b8e22cec71..33692b7a0c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Create as IndexCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -32,7 +33,7 @@ class Create extends IndexCreate public function __construct() { - $this->setContext(DATABASE_COLUMN_INDEX_CONTEXT); + $this->setContext(Context::DATABASE_COLUMN_INDEX); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index ed8c355075..80581f7a6f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Delete as IndexDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -33,7 +34,7 @@ class Delete extends IndexDelete public function __construct() { - $this->setContext(DATABASE_COLUMN_INDEX_CONTEXT); + $this->setContext(Context::DATABASE_COLUMN_INDEX); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index cd689316e6..d78761459a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Get as IndexGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -29,7 +30,7 @@ class Get extends IndexGet public function __construct() { - $this->setContext(DATABASE_COLUMN_INDEX_CONTEXT); + $this->setContext(Context::DATABASE_COLUMN_INDEX); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index 3d0e7d5139..b16e328f27 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\XList as IndexXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -29,7 +30,7 @@ class XList extends IndexXList public function __construct() { - $this->setContext(DATABASE_COLUMN_INDEX_CONTEXT); + $this->setContext(Context::DATABASE_COLUMN_INDEX); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 967e53539c..99fbe99a34 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Logs\XList as CollectionLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -25,7 +26,7 @@ class XList extends CollectionLogXList public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 881e562c10..5eb36598b2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as DocumentCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -39,7 +40,7 @@ class Create extends DocumentCreate public function __construct() { - $this->setContext(DATABASE_ROWS_CONTEXT); + $this->setContext(Context::DATABASE_ROWS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php index e40046bdf9..799f3cde05 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DocumentDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -34,7 +35,7 @@ class Delete extends DocumentDelete public function __construct() { - $this->setContext(DATABASE_ROWS_CONTEXT); + $this->setContext(Context::DATABASE_ROWS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php index 5ff02e96e8..8a8f9f3062 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as DocumentGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -30,7 +31,7 @@ class Get extends DocumentGet public function __construct() { - $this->setContext(DATABASE_ROWS_CONTEXT); + $this->setContext(Context::DATABASE_ROWS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php index 29a92413f9..62466eac13 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Logs; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Logs\XList as DocumentLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -25,7 +26,7 @@ class XList extends DocumentLogXList public function __construct() { - $this->setContext(DATABASE_ROWS_CONTEXT); + $this->setContext(Context::DATABASE_ROWS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index 3c98d3c499..798bfddf51 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Update as DocumentUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -31,7 +32,7 @@ class Update extends DocumentUpdate public function __construct() { - $this->setContext(DATABASE_ROWS_CONTEXT); + $this->setContext(Context::DATABASE_ROWS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php index c74a87c842..1175515e5c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\XList as DocumentXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -30,7 +31,7 @@ class XList extends DocumentXList public function __construct() { - $this->setContext(DATABASE_ROWS_CONTEXT); + $this->setContext(Context::DATABASE_ROWS); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index 70ac9d7b7c..14798f8513 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Update as CollectionUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -31,7 +32,7 @@ class Update extends CollectionUpdate public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index e67850e361..1baebb23f2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Usage; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Usage\Get as CollectionUsageGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -29,7 +30,7 @@ class Get extends CollectionUsageGet public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index b2f102cc55..1086f8f80e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\XList as CollectionXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -30,7 +31,7 @@ class XList extends CollectionXList public function __construct() { - $this->setContext(DATABASE_TABLES_CONTEXT); + $this->setContext(Context::DATABASE_TABLES); $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) From 95bc85f18d455111a80ebfb268ef788e1230123e Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 14:31:28 +0530 Subject: [PATCH 122/173] add: upsert for collections and tables. --- .../Collections/Documents/Upsert.php | 297 ++++++++++++++++++ .../Http/Databases/Tables/Rows/Upsert.php | 78 +++++ .../Services/Registry/Collections.php | 2 + .../Databases/Services/Registry/Tables.php | 2 + 4 files changed, 379 insertions(+) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php new file mode 100644 index 0000000000..efd950e846 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -0,0 +1,297 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') + ->desc('Upsert document') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].upsert') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'document.upsert') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', [ + new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/upsert-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + ), + ]) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new CustomId(), 'Document ID.') + ->param('data', [], new JSON(), 'Document data as JSON object. Include all required attributes of the document to be created or updated.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback($this->action(...)); + } + + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + + if (empty($data) && \is_null($permissions)) { + throw new Exception($this->getMissingPayloadException()); + } + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception($this->getParentNotFoundException()); + } + + // Map aggregate permissions into the multiple permissions they represent. + $permissions = Permission::aggregate($permissions, [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]); + + // Users can only manage their own roles, API keys and Admin users can manage any + $roles = Authorization::getRoles(); + if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + } + } + } + } + + $data['$id'] = $documentId; + $data['$permissions'] = $permissions; + $newDocument = new Document($data); + + $operations = 0; + + $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, &$operations) { + + $operations++; + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) + ); + + foreach ($relations as &$relation) { + // If the relation is an array it can be either update or create a child document. + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } + if ($relation instanceof Document) { + $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), + $relation->getId() + )); + $relation->removeAttribute('$collectionId'); + $relation->removeAttribute('$databaseId'); + // Attribute $collection is required for Utopia. + $relation->setAttribute( + '$collection', + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence() + ); + + if ($oldDocument->isEmpty()) { + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { + $relation['$id'] = ID::unique(); + } + } + $setCollection($relatedCollection, $relation); + } + } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } + } + }); + + $setCollection($collection, $newDocument); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); + + $upserted = []; + try { + $dbForProject->createOrUpdateDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + [$newDocument], + onNext: function (Document $document) use (&$upserted) { + $upserted[] = $document; + }, + ); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + } + + $document = $upserted[0]; + // Add $collectionId and $databaseId for all documents + $processDocument = function (Document $table, Document $document) use (&$processDocument, $dbForProject, $database) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $table->getId()); + + $relationships = \array_filter( + $table->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processDocument($relatedCollection, $relation); + } + } + } + }; + + $processDocument($collection, $document); + + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setParam('rowId', $document->getId()) + ->setContext($this->getCollectionsEventsContext(), $collection) + ->setPayload($response->getPayload(), sensitive: $relationships); + + $response->dynamic( + $document, + $this->getResponseModel() + ); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php new file mode 100644 index 0000000000..b0be7350b7 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php @@ -0,0 +1,78 @@ +setContext(Context::DATABASE_ROWS); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') + ->desc('Upsert row') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].upsert') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'row.upsert') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', [ + new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/upsert-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + ), + ]) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('data', [], new JSON(), 'Row data as JSON object. Include all required columns of the row to be created or updated.', true) + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback($this->action(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php index 7b629bca44..33d972e0f1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -31,6 +31,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Cre use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DeleteDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as GetDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Update as UpdateDocument; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Upsert as UpsertDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\XList as ListDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Get as GetCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Create as CreateIndex; @@ -78,6 +79,7 @@ class Collections extends Base $service->addAction(CreateDocument::getName(), new CreateDocument()); $service->addAction(GetDocument::getName(), new GetDocument()); $service->addAction(UpdateDocument::getName(), new UpdateDocument()); + $service->addAction(UpsertDocument::getName(), new UpsertDocument()); $service->addAction(DeleteDocument::getName(), new DeleteDocument()); $service->addAction(ListDocuments::getName(), new ListDocuments()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php index d881303c1e..0a8d8c0cfd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -38,6 +38,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Delete as Del use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Get as GetRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Logs\XList as ListRowLogs; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Update as UpdateRow; +use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Upsert as UpsertRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\XList as ListRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Update as UpdateTable; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Usage\Get as GetTableUsage; @@ -135,6 +136,7 @@ class Tables extends Base $service->addAction(CreateRow::getName(), new CreateRow()); $service->addAction(GetRow::getName(), new GetRow()); $service->addAction(UpdateRow::getName(), new UpdateRow()); + $service->addAction(UpsertRow::getName(), new UpsertRow()); $service->addAction(DeleteRow::getName(), new DeleteRow()); $service->addAction(ListRows::getName(), new ListRows()); $service->addAction(ListRowLogs::getName(), new ListRowLogs()); From db128d1fb4fed8d657f5a1313ce4b0697cc47ef0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 14:39:47 +0530 Subject: [PATCH 123/173] update: better syntax for callbacks. --- .../Http/Databases/Collections/Attributes/Boolean/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Boolean/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Datetime/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Datetime/Update.php | 2 +- .../Databases/Http/Databases/Collections/Attributes/Delete.php | 2 +- .../Http/Databases/Collections/Attributes/Email/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Email/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Enum/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Enum/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Float/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Float/Update.php | 2 +- .../Databases/Http/Databases/Collections/Attributes/Get.php | 2 +- .../Http/Databases/Collections/Attributes/IP/Create.php | 2 +- .../Http/Databases/Collections/Attributes/IP/Update.php | 2 +- .../Http/Databases/Collections/Attributes/Integer/Create.php | 2 +- .../Http/Databases/Collections/Attributes/Integer/Update.php | 2 +- .../Databases/Collections/Attributes/Relationship/Create.php | 2 +- .../Databases/Collections/Attributes/Relationship/Update.php | 2 +- .../Http/Databases/Collections/Attributes/String/Create.php | 2 +- .../Http/Databases/Collections/Attributes/String/Update.php | 2 +- .../Http/Databases/Collections/Attributes/URL/Create.php | 2 +- .../Http/Databases/Collections/Attributes/URL/Update.php | 2 +- .../Databases/Http/Databases/Collections/Attributes/XList.php | 2 +- .../Modules/Databases/Http/Databases/Collections/Create.php | 2 +- .../Modules/Databases/Http/Databases/Collections/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Create.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Get.php | 2 +- .../Http/Databases/Collections/Documents/Logs/XList.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Update.php | 2 +- .../Databases/Http/Databases/Collections/Documents/XList.php | 2 +- .../Modules/Databases/Http/Databases/Collections/Get.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Create.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Get.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/XList.php | 2 +- .../Modules/Databases/Http/Databases/Collections/Logs/XList.php | 2 +- .../Modules/Databases/Http/Databases/Collections/Update.php | 2 +- .../Modules/Databases/Http/Databases/Collections/Usage/Get.php | 2 +- .../Modules/Databases/Http/Databases/Collections/XList.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Create.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Delete.php | 2 +- src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Logs/XList.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Boolean/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Boolean/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Datetime/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Datetime/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Columns/Delete.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Email/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Email/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Enum/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Enum/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Float/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Float/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Columns/Get.php | 2 +- .../Databases/Http/Databases/Tables/Columns/IP/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/IP/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Integer/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/Integer/Update.php | 2 +- .../Http/Databases/Tables/Columns/Relationship/Create.php | 2 +- .../Http/Databases/Tables/Columns/Relationship/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/String/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/String/Update.php | 2 +- .../Databases/Http/Databases/Tables/Columns/URL/Create.php | 2 +- .../Databases/Http/Databases/Tables/Columns/URL/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Columns/XList.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Create.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Delete.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Get.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Indexes/Create.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Indexes/Delete.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Indexes/Get.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Indexes/XList.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Logs/XList.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Create.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Delete.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Get.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/XList.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Usage/Get.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/XList.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Update.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Usage/Get.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Usage/XList.php | 2 +- .../Platform/Modules/Databases/Http/Databases/XList.php | 2 +- src/Appwrite/Platform/Modules/Databases/Workers/Databases.php | 2 +- 89 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php index 7eb6a109c0..b1fddc0371 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php @@ -66,7 +66,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php index 9930ebdd8a..7aa2360fb8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php @@ -66,7 +66,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php index e3e0cc359b..fd54fe0843 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php @@ -67,7 +67,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php index eac9ba278c..17818fec2d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php @@ -67,7 +67,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 242f6a4ea0..4582500091 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -66,7 +66,7 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php index 80309609b8..80f0270ea3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php @@ -67,7 +67,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php index 5c35084508..2f14d8a361 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php @@ -67,7 +67,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php index 8e4ad25f75..006de12e91 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php @@ -70,7 +70,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php index be04a1ef9a..c2f6d7696c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php @@ -69,7 +69,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php index 4e3a8adf75..35e9bb68a1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php @@ -71,7 +71,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php index 13a90310e1..2fdcf949f1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php @@ -69,7 +69,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index ba91af54a2..5ce63982b1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -66,7 +66,7 @@ class Get extends Action ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php index 4bf5edc09d..c62e2add01 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php @@ -67,7 +67,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php index 380fa01123..a3e1ea5a61 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php @@ -67,7 +67,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php index dc28e41264..69272b6aea 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php @@ -71,7 +71,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php index 9e864e8b62..9c05382173 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php @@ -69,7 +69,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index 0bbb3faa67..fa8d0ad750 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -80,7 +80,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php index b489f92c3d..da2d0b9b08 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php @@ -68,7 +68,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php index 6463d2cedf..9b5201c1a8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php @@ -72,7 +72,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php index e746edb766..b3f4cf3f03 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php @@ -70,7 +70,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php index a802bbf8af..2d72533cf1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php @@ -67,7 +67,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php index 9ca1f372dc..e5c4288d92 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php @@ -67,7 +67,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index 307cb98eff..b9f1450fa3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -60,7 +60,7 @@ class XList extends Action ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index b5e7bf5415..84c18e9890 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -75,7 +75,7 @@ class Create extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php index 67bddbc9ff..b8080ab471 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php @@ -62,7 +62,7 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index b55731a4f2..6a16644a2f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -117,7 +117,7 @@ class Create extends Action ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 9944de4e36..dc307071e4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -75,7 +75,7 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index da730246bf..3f7f74ee75 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -65,7 +65,7 @@ class Get extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 2856d9ddd0..4f4dad9882 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -71,7 +71,7 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index f69dfb3f98..3bc97e817b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -80,7 +80,7 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 02e2900ac8..0012b8339a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -66,7 +66,7 @@ class XList extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php index 9ff9e3e442..0359025fc5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php @@ -55,7 +55,7 @@ class Get extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index e688088640..30753a9434 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -74,7 +74,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index 6fe3e3f42c..bb0b7bc4ca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -68,7 +68,7 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index 8d5513e1c0..e29bbf6647 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -57,7 +57,7 @@ class Get extends Action ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index caf6c72559..13717034ce 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -62,7 +62,7 @@ class XList extends Action ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 6e9f86260c..2f9ac2c8c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -70,7 +70,7 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php index e1e8da7017..cc41d716d4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php @@ -68,7 +68,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php index 5d49be7723..0c565806cc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php @@ -61,7 +61,7 @@ class Get extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index 15705e835e..06a6d2cd39 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -63,7 +63,7 @@ class XList extends Action ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php index 6e4dc5e136..3b9efa2fca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php @@ -65,7 +65,7 @@ class Create extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $name, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php index 60df83bb77..b9b475f699 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php @@ -57,7 +57,7 @@ class Delete extends Action ->inject('queueForDatabase') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php index 67b61bceb2..c2c6a57da1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php @@ -49,7 +49,7 @@ class Get extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php index 56358aa723..a531110398 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php @@ -64,7 +64,7 @@ class XList extends Action ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php index e1633da062..b8675877f3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php @@ -65,6 +65,6 @@ class Create extends BooleanCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php index deead5e1d7..09d01cae21 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php @@ -67,6 +67,6 @@ class Update extends BooleanUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php index 4710031c63..34ead7c33d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php @@ -67,6 +67,6 @@ class Create extends DatetimeCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php index f30e627553..5b026cd373 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php @@ -69,6 +69,6 @@ class Update extends DatetimeUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php index d2f0c54839..567cb02169 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php @@ -64,6 +64,6 @@ class Delete extends AttributesDelete ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php index fc64c3b215..f5bb2f5220 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php @@ -66,6 +66,6 @@ class Create extends EmailCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php index c6425043b5..faba9279b5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php @@ -68,6 +68,6 @@ class Update extends EmailUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php index 338cec4f05..ec1d36a916 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php @@ -69,6 +69,6 @@ class Create extends EnumCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php index 6a35b955d2..ef2c829e0a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php @@ -71,6 +71,6 @@ class Update extends EnumUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php index 7e1a502f2e..32579017c6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php @@ -68,6 +68,6 @@ class Create extends FloatCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php index 05983a8153..4ed420cfa0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php @@ -70,6 +70,6 @@ class Update extends FloatUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php index a0d8a3ce76..ed28f96535 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php @@ -67,6 +67,6 @@ class Get extends AttributesGet ->param('key', '', new Key(), 'Column Key.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php index 1bae06a360..8acc47f051 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php @@ -66,6 +66,6 @@ class Create extends IPCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php index 8174ad7860..c43f132c13 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php @@ -68,6 +68,6 @@ class Update extends IPUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php index 2c6b7a84e7..7694e9d74f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php @@ -68,6 +68,6 @@ class Create extends IntegerCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php index 8375853e83..b3ce988830 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php @@ -70,6 +70,6 @@ class Update extends IntegerUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index 6d3f26902d..133f772605 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -78,6 +78,6 @@ class Create extends RelationshipCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php index 8bed5013a9..ef1d8c7a33 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php @@ -70,6 +70,6 @@ class Update extends RelationshipUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index 529f4f270d..e1914bed65 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -70,6 +70,6 @@ class Create extends StringCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php index 7450af9eff..f7ec773e3c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php @@ -71,6 +71,6 @@ class Update extends StringUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php index 07295b2d93..0e6fe7cff3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php @@ -66,6 +66,6 @@ class Create extends URLCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php index 990fbee742..6f3698c0cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php @@ -68,6 +68,6 @@ class Update extends URLUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index 41b1aee5c9..c6ceb35f48 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -56,6 +56,6 @@ class XList extends AttributesXList ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Columns::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 95ef8bea4a..c20865d110 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -68,6 +68,6 @@ class Create extends CollectionCreate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index bb9b859a78..85e52ac37d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -61,6 +61,6 @@ class Delete extends CollectionDelete ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index cdf7950a1c..473dfb6273 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -56,6 +56,6 @@ class Get extends CollectionGet ->param('tableId', '', new UID(), 'Table ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index 33692b7a0c..3605afd985 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -69,6 +69,6 @@ class Create extends IndexCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index 80581f7a6f..7cda1ce471 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -67,6 +67,6 @@ class Delete extends IndexDelete ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index d78761459a..3bee87de4a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -58,6 +58,6 @@ class Get extends IndexGet ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index b16e328f27..1af13dc82e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -58,6 +58,6 @@ class XList extends IndexXList ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 99fbe99a34..244cebe3ad 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -56,6 +56,6 @@ class XList extends CollectionLogXList ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 5eb36598b2..81d90a7708 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -108,6 +108,6 @@ class Create extends DocumentCreate ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php index 799f3cde05..5edf4e0b73 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php @@ -72,6 +72,6 @@ class Delete extends DocumentDelete ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php index 8a8f9f3062..74e1f90497 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php @@ -61,6 +61,6 @@ class Get extends DocumentGet ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php index 62466eac13..1cc5202152 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php @@ -57,6 +57,6 @@ class XList extends DocumentLogXList ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index 798bfddf51..dfe77c9075 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -71,6 +71,6 @@ class Update extends DocumentUpdate ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php index 1175515e5c..9d03603a0e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php @@ -60,6 +60,6 @@ class XList extends DocumentXList ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index 14798f8513..ab71634fb2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -67,6 +67,6 @@ class Update extends CollectionUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index 1baebb23f2..9fb5fc8c17 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -58,6 +58,6 @@ class Get extends CollectionUsageGet ->param('tableId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index 1086f8f80e..f6f48ace40 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -59,6 +59,6 @@ class XList extends CollectionXList ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php index 49c92a65a2..a0e440c657 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php @@ -58,7 +58,7 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $name, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index fab12a9735..1a85380767 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -55,7 +55,7 @@ class Get extends Action ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $databaseId, string $range, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index 48533935b3..2d551465db 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -52,7 +52,7 @@ class XList extends Action ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(string $range, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php index 8bb9184baa..dcf7e2d724 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php @@ -56,7 +56,7 @@ class XList extends Action ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') - ->callback([$this, 'action']); + ->callback($this->action(...)); } public function action(array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void diff --git a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 0cbddd2a52..2576765445 100644 --- a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -38,7 +38,7 @@ class Databases extends Action ->inject('dbForProject') ->inject('queueForRealtime') ->inject('log') - ->callback([$this, 'action']); + ->callback($this->action(...)); } /** From 9334b98308b16918b7e70145c68225c4bcc8c8dd Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 14:40:33 +0530 Subject: [PATCH 124/173] fix: duplicate call to collection creation. --- .../Modules/Databases/Http/Databases/Collections/Create.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index 84c18e9890..5edce7715f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -102,8 +102,6 @@ class Create extends Action 'name' => $name, 'search' => \implode(' ', [$collectionId, $name]), ])); - - $dbForProject->createCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), permissions: $permissions, documentSecurity: $documentSecurity); } catch (DuplicateException) { throw new Exception($this->getDuplicateException()); } catch (LimitException) { From d6b55730eb8b377b4f19a5b5c58ec4ef80467f9e Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 15:10:28 +0530 Subject: [PATCH 125/173] fix: string encryption attribute in model. --- .../Collections/Attributes/String/Create.php | 17 ++++++++++++++++- .../Databases/Tables/Columns/String/Create.php | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php index 9b5201c1a8..cc989f6687 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php @@ -72,6 +72,7 @@ class Create extends Action ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->callback($this->action(...)); } @@ -87,8 +88,20 @@ class Create extends Action UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, - Event $queueForEvents + Event $queueForEvents, + array $plan ): void { + if ($encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string ' . $this->getSdkGroup() . ' are not available on your plan. Please upgrade to create encrypted string ' . $this->getSdkGroup() . '.'); + } + + if ($encrypt && $size < APP_DATABASE_ENCRYPT_SIZE_MIN) { + throw new Exception( + Exception::GENERAL_BAD_REQUEST, + "Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters." + ); + } + // Ensure default fits in the given size $validator = new Text($size, 0); if (!is_null($default) && !$validator->isValid($default)) { @@ -118,6 +131,8 @@ class Create extends Action $queueForEvents ); + $attribute->setAttribute('encrypt', $encrypt); + $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) ->dynamic($attribute, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index e1914bed65..ed7989fbc3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -70,6 +70,7 @@ class Create extends StringCreate ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->callback($this->action(...)); } } From d502869c3d06f62be02da1384ba1c612fff8ec53 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 15:21:06 +0530 Subject: [PATCH 126/173] fix: index lengths. --- .../Http/Databases/Collections/Indexes/Create.php | 11 ++++++----- .../Http/Databases/Tables/Indexes/Create.php | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 30753a9434..1665549c92 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -22,6 +22,8 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; +use Utopia\Validator\Integer; +use Utopia\Validator\Nullable; use Utopia\Validator\WhiteList; class Create extends Action @@ -70,6 +72,7 @@ class Create extends Action ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) + ->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional: true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') @@ -77,7 +80,7 @@ class Create extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -103,7 +106,7 @@ class Create extends Action throw new Exception($this->getLimitException(), 'Index limit exceeded'); } - // Convert Document[] to array of attribute metadata + // Convert Document array to array of attribute metadata $oldAttributes = \array_map(fn ($a) => $a->getArrayCopy(), $collection->getAttribute('attributes')); $oldAttributes[] = [ @@ -138,9 +141,6 @@ class Create extends Action 'size' => 0 ]; - // lengths hidden by default - $lengths = []; - $contextType = $this->getParentContext(); foreach ($attributes as $i => $attribute) { // find attribute metadata in collection document @@ -191,6 +191,7 @@ class Create extends Action $dbForProject->getAdapter()->getMaxIndexLength(), $dbForProject->getAdapter()->getInternalIndexesKeys(), ); + if (!$validator->isValid($index)) { throw new Exception($this->getInvalidTypeException(), $validator->getDescription()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index 3605afd985..e57e962e87 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -15,6 +15,8 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; +use Utopia\Validator\Integer; +use Utopia\Validator\Nullable; use Utopia\Validator\WhiteList; class Create extends IndexCreate @@ -65,6 +67,7 @@ class Create extends IndexCreate ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) + ->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional: true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') From b1f7f08b9a17216efde50ce028d7bf7dfdc7ec67 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 16:06:15 +0530 Subject: [PATCH 127/173] remove: duplicate events. --- .../Http/Databases/Collections/Documents/Create.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 6a16644a2f..33406044f1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -367,12 +367,6 @@ class Create extends Action throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setContext('collection', $collection) - ->setContext('database', $database); - $queueForEvents ->setParam('databaseId', $databaseId) ->setContext('database', $database) From ffec769f4ada9b6019e55385aea1871646802575 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 16:16:55 +0530 Subject: [PATCH 128/173] fix: index lengths. --- .../Databases/Http/Databases/Collections/Indexes/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 1665549c92..9dbd7924c6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -164,7 +164,7 @@ class Create extends Action throw new Exception($this->getParentNotAvailableException(), "$contextType not available: " . $oldAttributes[$attributeIndex]['key']); } - $lengths[$i] = null; + $lengths[$i] ??= null; if ($attributeArray === true) { $lengths[$i] = Database::ARRAY_INDEX_LENGTH; From ad34cf3a624f2d8a7ef302aa6ae86c33028e222c Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 16:17:27 +0530 Subject: [PATCH 129/173] add: lengths to column indexes. --- src/Appwrite/Utopia/Response/Model/ColumnIndex.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php index 8c632266ef..bebc640fb2 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php @@ -42,6 +42,13 @@ class ColumnIndex extends Model 'example' => [], 'array' => true, ]) + ->addRule('lengths', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Index columns length.', + 'default' => [], + 'example' => [], + 'array' => true, + ]) ->addRule('orders', [ 'type' => self::TYPE_STRING, 'description' => 'Index orders.', From 44b8054ed7e831d70415c2e76695e9781b1a3605 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 16:55:15 +0530 Subject: [PATCH 130/173] add: upsert documents/rows. --- .../Collections/Documents/Bulk/Upsert.php | 141 ++++++++++++++++++ .../Databases/Tables/Rows/Bulk/Upsert.php | 74 +++++++++ .../Services/Registry/Collections.php | 2 + .../Databases/Services/Registry/Tables.php | 2 + 4 files changed, 219 insertions(+) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php new file mode 100644 index 0000000000..1e7d838fba --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -0,0 +1,141 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') + ->desc('Create or update documents') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'document.create') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', [ + new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/upsert-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON, + ) + ]) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', false, ['plan']) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->inject('plan') + ->callback($this->action(...)); + } + + public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $hasRelationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + if ($hasRelationships) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); + } + + foreach ($documents as $key => $document) { + $documents[$key] = new Document($document); + } + + $upserted = []; + + try { + $modified = $dbForProject->createOrUpdateDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documents, + onNext: function (Document $document) use ($plan, &$upserted) { + if (\count($upserted) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { + $upserted[] = $document; + } + }, + ); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (DuplicateException) { + throw new Exception($this->getDuplicateException()); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + } + + foreach ($upserted as $document) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified)) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); + + $response->dynamic(new Document([ + 'total' => $modified, + $this->getSdkGroup() => $upserted + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php new file mode 100644 index 0000000000..4a5b2a0fac --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php @@ -0,0 +1,74 @@ +setContext(Context::DATABASE_ROWS); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') + ->desc('Create or update rows') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'document.create') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', [ + new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/upsert-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_CREATED, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON, + ) + ]) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of row data as JSON objects. May contain partial rows.', false, ['plan']) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->inject('plan') + ->callback($this->action(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php index 33d972e0f1..ca6796c3e9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -27,6 +27,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\UR use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XList as ListAttributes; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CreateCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as DeleteCollection; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as UpsertDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as CreateDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DeleteDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as GetDocument; @@ -80,6 +81,7 @@ class Collections extends Base $service->addAction(GetDocument::getName(), new GetDocument()); $service->addAction(UpdateDocument::getName(), new UpdateDocument()); $service->addAction(UpsertDocument::getName(), new UpsertDocument()); + $service->addAction(UpsertDocuments::getName(), new UpsertDocuments()); $service->addAction(DeleteDocument::getName(), new DeleteDocument()); $service->addAction(ListDocuments::getName(), new ListDocuments()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php index 0a8d8c0cfd..2bd596bb1a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -33,6 +33,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Delete as use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Get as GetColumnIndex; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\XList as ListColumnIndexes; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs\XList as ListTableLogs; +use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Upsert as UpsertRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Create as CreateRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Delete as DeleteRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Get as GetRow; @@ -137,6 +138,7 @@ class Tables extends Base $service->addAction(GetRow::getName(), new GetRow()); $service->addAction(UpdateRow::getName(), new UpdateRow()); $service->addAction(UpsertRow::getName(), new UpsertRow()); + $service->addAction(UpsertRows::getName(), new UpsertRows()); $service->addAction(DeleteRow::getName(), new DeleteRow()); $service->addAction(ListRows::getName(), new ListRows()); $service->addAction(ListRowLogs::getName(), new ListRowLogs()); From a6ae715e3a6b748ee794f3765259ff337e447a20 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 16:55:31 +0530 Subject: [PATCH 131/173] update: descriptions. --- .../Databases/Http/Databases/Collections/Documents/Upsert.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Upsert.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index efd950e846..ebe30f6970 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -47,7 +47,7 @@ class Upsert extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') - ->desc('Upsert document') + ->desc('Create or update a document') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].upsert') ->label('scope', 'documents.write') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php index b0be7350b7..800efa9cde 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php @@ -37,7 +37,7 @@ class Upsert extends DocumentUpsert $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') - ->desc('Upsert row') + ->desc('Create or update a row') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].upsert') ->label('scope', 'documents.write') From 919f358f3fd5ded0363f8a34fe9b6bdd5a5c109b Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 16:58:18 +0530 Subject: [PATCH 132/173] fix: audit event. --- .../Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php index 4a5b2a0fac..9331e75aa8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php @@ -41,7 +41,7 @@ class Upsert extends DocumentsUpsert ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) - ->label('audits.event', 'document.create') + ->label('audits.event', 'row.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) From 1c87e765de8c563d7c6da1ecd739b1d75d3a76fc Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 17:12:43 +0530 Subject: [PATCH 133/173] add: delete documents/rows --- .../Collections/Documents/Bulk/Delete.php | 136 ++++++++++++++++++ .../Databases/Tables/Rows/Bulk/Delete.php | 71 +++++++++ .../Services/Registry/Collections.php | 2 + .../Databases/Services/Registry/Tables.php | 2 + 4 files changed, 211 insertions(+) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php new file mode 100644 index 0000000000..bee3b35599 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -0,0 +1,136 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') + ->desc('Delete documents') + ->groups(['api', 'database']) + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'documents.delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/delete-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->inject('plan') + ->callback($this->action(...)); + } + + public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void + { + $database = $dbForProject->getDocument('databases', $databaseId); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $hasRelationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + if ($hasRelationships) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + $documents = []; + + try { + $modified = $dbForProject->deleteDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $queries, + onNext: function (Document $document) use ($plan, &$documents) { + if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { + $documents[] = $document; + } + }, + ); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (RestrictedException) { + throw new Exception($this->getRestrictedException()); + } + + foreach ($documents as $document) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified)) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); + + $response->dynamic(new Document([ + 'total' => $modified, + $this->getSdkGroup() => $documents, + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php new file mode 100644 index 0000000000..73f3016936 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php @@ -0,0 +1,71 @@ +setContext(Context::DATABASE_ROWS); + + $this + ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/:databaseId/tables/:collectionId/rows') + ->desc('Delete rows') + ->groups(['api', 'database']) + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'rows.delete') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/delete-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->inject('plan') + ->callback($this->action(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php index ca6796c3e9..65953c7992 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -27,6 +27,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\UR use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XList as ListAttributes; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CreateCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as DeleteCollection; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Delete as DeleteDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as UpsertDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as CreateDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DeleteDocument; @@ -83,6 +84,7 @@ class Collections extends Base $service->addAction(UpsertDocument::getName(), new UpsertDocument()); $service->addAction(UpsertDocuments::getName(), new UpsertDocuments()); $service->addAction(DeleteDocument::getName(), new DeleteDocument()); + $service->addAction(DeleteDocuments::getName(), new DeleteDocuments()); $service->addAction(ListDocuments::getName(), new ListDocuments()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php index 2bd596bb1a..87bf51512e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -33,6 +33,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Delete as use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Get as GetColumnIndex; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\XList as ListColumnIndexes; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs\XList as ListTableLogs; +use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Delete as DeleteRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Upsert as UpsertRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Create as CreateRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Delete as DeleteRow; @@ -140,6 +141,7 @@ class Tables extends Base $service->addAction(UpsertRow::getName(), new UpsertRow()); $service->addAction(UpsertRows::getName(), new UpsertRows()); $service->addAction(DeleteRow::getName(), new DeleteRow()); + $service->addAction(DeleteRows::getName(), new DeleteRows()); $service->addAction(ListRows::getName(), new ListRows()); $service->addAction(ListRowLogs::getName(), new ListRowLogs()); } From 2994cdf874b3975a678b7db9e893cdd446e5b89b Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 17:14:50 +0530 Subject: [PATCH 134/173] update: model. --- src/Appwrite/Utopia/Response/Model/Row.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Row.php b/src/Appwrite/Utopia/Response/Model/Row.php index c30984ab7a..b7750feccd 100644 --- a/src/Appwrite/Utopia/Response/Model/Row.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -36,6 +36,12 @@ class Row extends Any 'default' => '', 'example' => '5e5ea5c16897e', ]) + ->addRule('$sequence', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Row automatically incrementing ID.', + 'default' => 0, + 'example' => 1, + ]) ->addRule('$tableId', [ 'type' => self::TYPE_STRING, 'description' => 'Table ID.', @@ -71,7 +77,6 @@ class Row extends Any public function filter(DatabaseDocument $document): DatabaseDocument { - $document->removeAttribute('$internalId'); $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); From 3426847b177b080f44375b4ce5f85ed1622de181 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 17:31:37 +0530 Subject: [PATCH 135/173] add: update documents/rows. --- .../Collections/Documents/Bulk/Update.php | 158 ++++++++++++++++++ .../Databases/Tables/Rows/Bulk/Update.php | 70 ++++++++ .../Services/Registry/Collections.php | 2 + .../Databases/Services/Registry/Tables.php | 2 + 4 files changed, 232 insertions(+) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php new file mode 100644 index 0000000000..23c8d5d58e --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -0,0 +1,158 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') + ->desc('Update documents') + ->groups(['api', 'database']) + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'documents.update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->inject('plan') + ->callback($this->action(...)); + } + + public function action(string $databaseId, string $collectionId, string|array $data, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void + { + $data = \is_string($data) + ? \json_decode($data, true) + : $data; + + if (empty($data)) { + throw new Exception($this->getMissingPayloadException()); + } + + $database = $dbForProject->getDocument('databases', $databaseId); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + $hasRelationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + if ($hasRelationships) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk update is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + if ($data['$permissions']) { + $validator = new Permissions(); + if (!$validator->isValid($data['$permissions'])) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); + } + } + + $documents = []; + + try { + $modified = $dbForProject->updateDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + new Document($data), + $queries, + onNext: function (Document $document) use ($plan, &$documents) { + if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { + $documents[] = $document; + } + }, + ); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + } + + foreach ($documents as $document) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified)) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); + + $response->dynamic(new Document([ + 'total' => $modified, + $this->getSdkGroup() => $documents + ]), $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php new file mode 100644 index 0000000000..95a0819dfd --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php @@ -0,0 +1,70 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') + ->desc('Update rows') + ->groups(['api', 'database']) + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'rows.update') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/update-documents.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('data', [], new JSON(), 'Row data as JSON object. Include only column and value pairs to be updated.', true) + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForStatsUsage') + ->inject('plan') + ->callback($this->action(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php index 65953c7992..415eaeaf85 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -28,6 +28,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XL use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CreateCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as DeleteCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Delete as DeleteDocuments; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Update as UpdateDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as UpsertDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as CreateDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DeleteDocument; @@ -81,6 +82,7 @@ class Collections extends Base $service->addAction(CreateDocument::getName(), new CreateDocument()); $service->addAction(GetDocument::getName(), new GetDocument()); $service->addAction(UpdateDocument::getName(), new UpdateDocument()); + $service->addAction(UpdateDocuments::getName(), new UpdateDocuments()); $service->addAction(UpsertDocument::getName(), new UpsertDocument()); $service->addAction(UpsertDocuments::getName(), new UpsertDocuments()); $service->addAction(DeleteDocument::getName(), new DeleteDocument()); diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php index 87bf51512e..87e5f80e3f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -34,6 +34,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Get as Get use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\XList as ListColumnIndexes; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs\XList as ListTableLogs; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Delete as DeleteRows; +use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Update as UpdateRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Upsert as UpsertRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Create as CreateRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Delete as DeleteRow; @@ -138,6 +139,7 @@ class Tables extends Base $service->addAction(CreateRow::getName(), new CreateRow()); $service->addAction(GetRow::getName(), new GetRow()); $service->addAction(UpdateRow::getName(), new UpdateRow()); + $service->addAction(UpdateRows::getName(), new UpdateRows()); $service->addAction(UpsertRow::getName(), new UpsertRow()); $service->addAction(UpsertRows::getName(), new UpsertRows()); $service->addAction(DeleteRow::getName(), new DeleteRow()); From 90d18a9bd5949760c12aed6bfe687778b81cea9e Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 18:24:54 +0530 Subject: [PATCH 136/173] add: increment, decrement document/row attribute/column; fixes: misc. --- .../Databases/Collections/Attributes/Get.php | 12 +- .../Collections/Documents/Action.php | 20 +++ .../Documents/Attribute/Decrement.php | 125 +++++++++++++++++ .../Documents/Attribute/Increment.php | 126 ++++++++++++++++++ .../Databases/Tables/Rows/Bulk/Delete.php | 2 +- .../Tables/Rows/Column/Decrement.php | 72 ++++++++++ .../Tables/Rows/Column/Increment.php | 72 ++++++++++ .../Services/Registry/Collections.php | 5 + .../Databases/Services/Registry/Tables.php | 4 + .../Utopia/Response/Model/ColumnString.php | 7 + 10 files changed, 440 insertions(+), 5 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index 5ce63982b1..d49639e049 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -86,14 +86,18 @@ class Get extends Action throw new Exception($this->getNotFoundException()); } - foreach ($attribute->getAttribute('options', []) as $optKey => $optVal) { - $attribute->setAttribute($optKey, $optVal); - } - $type = $attribute->getAttribute('type'); $format = $attribute->getAttribute('format'); + $options = $attribute->getAttribute('options', []); + $filters = $attribute->getAttribute('filters', []); + foreach ($options as $key => $option) { + $attribute->setAttribute($key, $option); + } + $model = $this->getCorrectModel($type, $format); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + $response->dynamic($attribute, $model); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 341302f779..c0e71d3730 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -96,6 +96,16 @@ abstract class Action extends UtopiaAction : Exception::TABLE_NOT_FOUND; } + /** + * Get the appropriate attribute/column not found exception. + */ + final protected function getStructureNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_NOT_FOUND + : Exception::COLUMN_NOT_FOUND; + } + /** * Get the appropriate not found exception. */ @@ -156,6 +166,16 @@ abstract class Action extends UtopiaAction : Exception::ROW_MISSING_DATA; } + /** + * Get the exception to throw when the resource limit is exceeded. + */ + final protected function getLimitException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_LIMIT_EXCEEDED + : Exception::COLUMN_LIMIT_EXCEEDED; + } + /** * Get the appropriate missing payload exception. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php new file mode 100644 index 0000000000..9a8da5858c --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -0,0 +1,125 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/decrement') + ->desc('Decrement document attribute') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].decrement') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'documents.decrement') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/decrement-document-attribute.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('attribute', '', new Key(), 'Attribute key.') + ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) + ->param('min', null, new Numeric(), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback($this->action(...)); + } + + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + try { + $document = $dbForProject->decreaseDocumentAttribute( + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $documentId, + attribute: $attribute, + value: $value, + min: $min + ); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (NotFoundException) { + throw new Exception($this->getStructureNotFoundException()); + } catch (LimitException) { + throw new Exception($this->getLimitException(), $this->getSdkNamespace() . ' "' . $attribute . '" has reached the minimum value of ' . $min); + } catch (TypeException) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, $this->getSdkNamespace() . ' "' . $attribute . '" is not a number'); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam('collectionId', $collectionId) + ->setParam('tableId', $collectionId) + ->setContext($this->getCollectionsEventsContext(), $collection); + + $response->dynamic($document, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php new file mode 100644 index 0000000000..b65ca3c285 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -0,0 +1,126 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/increment') + ->desc('Increment document attribute') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].increment') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'documents.increment') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/increment-document-attribute.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('attribute', '', new Key(), 'Attribute key.') + ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) + ->param('max', null, new Numeric(), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback($this->action(...)); + } + + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + if ($collection->isEmpty()) { + throw new Exception($this->getParentNotFoundException()); + } + + try { + $document = $dbForProject->increaseDocumentAttribute( + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $documentId, + attribute: $attribute, + value: $value, + max: $max + ); + } catch (ConflictException) { + throw new Exception($this->getConflictException()); + } catch (NotFoundException) { + // todo: @itznotabug what do we name this exception now? + throw new Exception($this->getStructureNotFoundException()); + } catch (LimitException) { + throw new Exception($this->getLimitException(), $this->getSdkNamespace() . ' "' . $attribute . '" has reached the maximum value of ' . $max); + } catch (TypeException) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, $this->getSdkNamespace() . ' "' . $attribute . '" is not a number'); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setContext('database', $database) + ->setParam('collectionId', $collectionId) + ->setParam('tableId', $collectionId) + ->setContext($this->getCollectionsEventsContext(), $collection); + + $response->dynamic($document, $this->getResponseModel()); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php index 73f3016936..9ddff818e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php @@ -35,7 +35,7 @@ class Delete extends DocumentsDelete $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) - ->setHttpPath('/v1/databases/:databaseId/tables/:collectionId/rows') + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Delete rows') ->groups(['api', 'database']) ->label('scope', 'documents.write') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php new file mode 100644 index 0000000000..5b2cf17f91 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php @@ -0,0 +1,72 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/:column/decrement') + ->desc('Decrement row column') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].decrement') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'rows.decrement') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/decrement-document-attribute.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('column', '', new Key(), 'Column key.') + ->param('value', 1, new Numeric(), 'Value to increment the column by. The value must be a number.', true) + ->param('min', null, new Numeric(), 'Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback($this->action(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php new file mode 100644 index 0000000000..4dcd05cc25 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php @@ -0,0 +1,72 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/:column/increment') + ->desc('Increment row column') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].increment') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'rows.increment') + ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: $this->getSdkNamespace(), + group: $this->getSdkGroup(), + name: self::getName(), + description: '/docs/references/databases/increment-document-attribute.md', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: SwooleResponse::STATUS_CODE_OK, + model: $this->getResponseModel(), + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('tableId', '', new UID(), 'Table ID.') + ->param('rowId', '', new UID(), 'Row ID.') + ->param('column', '', new Key(), 'Column key.') + ->param('value', 1, new Numeric(), 'Value to increment the column by. The value must be a number.', true) + ->param('max', null, new Numeric(), 'Maximum value for the column. If the current value is greater than this value, an error will be thrown.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->callback($this->action(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php index 415eaeaf85..d036d2fd7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -27,6 +27,8 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\UR use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XList as ListAttributes; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CreateCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as DeleteCollection; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute\Decrement as DecrementDocumentAttribute; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute\Increment as IncrementDocumentAttribute; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Delete as DeleteDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Update as UpdateDocuments; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as UpsertDocuments; @@ -88,6 +90,9 @@ class Collections extends Base $service->addAction(DeleteDocument::getName(), new DeleteDocument()); $service->addAction(DeleteDocuments::getName(), new DeleteDocuments()); $service->addAction(ListDocuments::getName(), new ListDocuments()); + $service->addAction(IncrementDocumentAttribute::getName(), new IncrementDocumentAttribute()); + $service->addAction(DecrementDocumentAttribute::getName(), new DecrementDocumentAttribute()); + } private function registerAttributeActions(Service $service): void diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php index 87e5f80e3f..4ddadeece6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php @@ -36,6 +36,8 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs\XList as List use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Delete as DeleteRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Update as UpdateRows; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Upsert as UpsertRows; +use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Column\Decrement as DecrementRowColumn; +use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Column\Increment as IncrementRowColumn; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Create as CreateRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Delete as DeleteRow; use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Get as GetRow; @@ -146,5 +148,7 @@ class Tables extends Base $service->addAction(DeleteRows::getName(), new DeleteRows()); $service->addAction(ListRows::getName(), new ListRows()); $service->addAction(ListRowLogs::getName(), new ListRowLogs()); + $service->addAction(IncrementRowColumn::getName(), new IncrementRowColumn()); + $service->addAction(DecrementRowColumn::getName(), new DecrementRowColumn()); } } diff --git a/src/Appwrite/Utopia/Response/Model/ColumnString.php b/src/Appwrite/Utopia/Response/Model/ColumnString.php index f962e51c37..f3a876b7a9 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnString.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnString.php @@ -24,6 +24,13 @@ class ColumnString extends Column 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this column is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } From 1f31998092ce4c03fc078cac14066fd3fc25fe69 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 19:48:28 +0530 Subject: [PATCH 137/173] =?UTF-8?q?fix:=20route=20name=20=F0=9F=98=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Databases/Http/Databases/Tables/Rows/Bulk/Update.php | 2 +- .../Modules/Databases/Http/Databases/Tables/Rows/Update.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php index 95a0819dfd..53e7c3c152 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php @@ -21,7 +21,7 @@ class Update extends DocumentsUpdate public static function getName(): string { - return 'updateRow'; + return 'updateRows'; } protected function getResponseModel(): string diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index dfe77c9075..d37f5cc92f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -62,7 +62,7 @@ class Update extends DocumentUpdate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Collection ID.') + ->param('tableId', '', new UID(), 'Table ID.') ->param('rowId', '', new UID(), 'Row ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) From b248c9b640f6ecca22e81684c58a4f29efdb418d Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 19:58:28 +0530 Subject: [PATCH 138/173] =?UTF-8?q?fix:=20a=20bunch=20of=20tests=20?= =?UTF-8?q?=F0=9F=9A=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Databases/Collections/DatabasesBase.php | 12 +- .../Collections/DatabasesCustomServerTest.php | 4 +- .../Databases/Tables/DatabasesBase.php | 681 ++++++++- .../Tables/DatabasesCustomServerTest.php | 1231 +++++++++++++++-- 4 files changed, 1782 insertions(+), 146 deletions(-) diff --git a/tests/e2e/Services/Databases/Collections/DatabasesBase.php b/tests/e2e/Services/Databases/Collections/DatabasesBase.php index 956b87e13d..6b0aca1512 100644 --- a/tests/e2e/Services/Databases/Collections/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesBase.php @@ -1421,7 +1421,6 @@ trait DatabasesBase return $data; } - /** * @depends testCreateAttributes */ @@ -2079,7 +2078,6 @@ trait DatabasesBase return ['documents' => $documents['body']['documents'], 'databaseId' => $databaseId]; } - /** * @depends testListDocuments */ @@ -2830,7 +2828,7 @@ trait DatabasesBase return $data; } - public function testInvalidDocumentStructure() + public function testInvalidDocumentStructure(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -3693,7 +3691,7 @@ trait DatabasesBase $this->assertCount(1, $documentsUser2['body']['documents']); } - public function testEnforceCollectionPermissions() + public function testEnforceCollectionPermissions(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -3986,7 +3984,7 @@ trait DatabasesBase /** * @depends testUniqueIndexDuplicate */ - public function testPersistantCreatedAt(array $data): array + public function testPersistentCreatedAt(array $data): array { $headers = $this->getSide() === 'client' ? array_merge([ 'content-type' => 'application/json', @@ -5498,13 +5496,11 @@ trait DatabasesBase ]), ['min' => 7]); $this->assertEquals(400, $err['headers']['status-code']); - // Test type error on non-numeric attribut + // Test type error on non-numeric attribute $typeErr = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]), ['value' => 'not-a-number']); $this->assertEquals(400, $typeErr['headers']['status-code']); } - - } diff --git a/tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php index 84f7d43593..21382b8302 100644 --- a/tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Collections/DatabasesCustomServerTest.php @@ -3651,7 +3651,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $doc3['headers']['status-code']); } - public function createRelationshipCollections() + public function createRelationshipCollections(): void { // Prepare the database with collections and relationships $database = $this->client->call(Client::METHOD_POST, '/databases', [ @@ -3700,7 +3700,7 @@ class DatabasesCustomServerTest extends Scope \sleep(2); } - public function cleanupRelationshipCollection() + public function cleanupRelationshipCollection(): void { $this->client->call(Client::METHOD_DELETE, '/databases/database1', [ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Databases/Tables/DatabasesBase.php b/tests/e2e/Services/Databases/Tables/DatabasesBase.php index ef35d3c962..a0b6be2fbe 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesBase.php @@ -39,7 +39,7 @@ trait DatabasesBase /** * @depends testCreateDatabase */ - public function testCreateCollection(array $data): array + public function testCreateTable(array $data): array { $databaseId = $data['databaseId']; /** @@ -85,9 +85,9 @@ trait DatabasesBase } /** - * @depends testCreateCollection + * @depends testCreateTable */ - public function testConsoleProject(array $data) + public function testConsoleProject(array $data): void { if ($this->getSide() === 'server') { // Server side can't get past the invalid key check anyway @@ -121,9 +121,9 @@ trait DatabasesBase } /** - * @depends testCreateCollection + * @depends testCreateTable */ - public function testDisableCollection(array $data): void + public function testDisableTable(array $data): void { $databaseId = $data['databaseId']; /** @@ -190,9 +190,9 @@ trait DatabasesBase } /** - * @depends testCreateCollection + * @depends testCreateTable */ - public function testCreateAttributes(array $data): array + public function testCreateColumns(array $data): array { $databaseId = $data['databaseId']; @@ -375,9 +375,9 @@ trait DatabasesBase } /** - * @depends testCreateAttributes + * @depends testCreateColumns */ - public function testListAttributes(array $data): void + public function testListColumns(array $data): void { $databaseId = $data['databaseId']; $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns', array_merge([ @@ -407,7 +407,7 @@ trait DatabasesBase /** * @depends testCreateDatabase */ - public function testPatchAttribute(array $data): void + public function testPatchColumn(array $data): void { $databaseId = $data['databaseId']; @@ -471,7 +471,7 @@ trait DatabasesBase $this->assertStringContainsString('Index length is longer than the maximum: 76', $attribute['body']['message']); } - public function testUpdateAttributeEnum(): void + public function testUpdateColumnEnum(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', [ 'content-type' => 'application/json', @@ -529,9 +529,9 @@ trait DatabasesBase } /** - * @depends testCreateAttributes + * @depends testCreateColumns */ - public function testAttributeResponseModels(array $data): array + public function testColumnResponseModels(array $data): array { $databaseId = $data['databaseId']; $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ @@ -1196,7 +1196,7 @@ trait DatabasesBase } /** - * @depends testCreateAttributes + * @depends testCreateColumns */ public function testCreateIndexes(array $data): array { @@ -1421,6 +1421,99 @@ trait DatabasesBase return $data; } + /** + * @depends testCreateColumns + */ + public function testGetIndexByKeyWithLengths(array $data): void + { + $databaseId = $data['databaseId']; + $tableId = $data['moviesId']; + + // Test case for valid lengths + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$tableId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthTestIndex', + 'type' => 'key', + 'attributes' => ['title','description'], + 'lengths' => [128,200] + ]); + $this->assertEquals(202, $create['headers']['status-code']); + + // Fetch index and check correct lengths + $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$tableId}/indexes/lengthTestIndex", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + $this->assertEquals(200, $index['headers']['status-code']); + $this->assertEquals('lengthTestIndex', $index['body']['key']); + $this->assertEquals([128, 200], $index['body']['lengths']); + + // Test case for lengths array overriding + // set a length for an array attribute, it should get overriden with Database::ARRAY_INDEX_LENGTH + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$tableId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthOverrideTestIndex', + 'type' => 'key', + 'attributes' => ['actors'], + 'lengths' => [120] + ]); + $this->assertEquals(202, $create['headers']['status-code']); + + $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$tableId}/indexes/lengthOverrideTestIndex", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + $this->assertEquals([Database::ARRAY_INDEX_LENGTH], $index['body']['lengths']); + + // Test case for count of lengths greater than attributes (should throw 400) + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$tableId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthCountExceededIndex', + 'type' => 'key', + 'attributes' => ['title'], + 'lengths' => [128, 128] + ]); + $this->assertEquals(400, $create['headers']['status-code']); + + // Test case for lengths exceeding total of 768 + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$tableId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthTooLargeIndex', + 'type' => 'key', + 'attributes' => ['title','description','tagline','actors'], + 'lengths' => [256,256,256,20] + ]); + + $this->assertEquals(400, $create['headers']['status-code']); + + // Test case for negative length values + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$tableId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'negativeLengthIndex', + 'type' => 'key', + 'attributes' => ['title'], + 'lengths' => [-1] + ]); + $this->assertEquals(400, $create['headers']['status-code']); + } + /** * @depends testCreateIndexes */ @@ -1455,7 +1548,7 @@ trait DatabasesBase /** * @depends testCreateIndexes */ - public function testCreateDocument(array $data): array + public function testCreateRow(array $data): array { $databaseId = $data['databaseId']; $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ @@ -1596,9 +1689,311 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateIndexes */ - public function testListDocuments(array $data): array + public function testUpsertRow(array $data): void + { + $databaseId = $data['databaseId']; + $rowId = ID::unique(); + $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Ragnarok', + 'releaseYear' => 2000 + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + ]); + + $this->assertEquals(200, $document['headers']['status-code']); + $this->assertCount(3, $document['body']['$permissions']); + $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Thor: Ragnarok', $document['body']['title']); + + $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Love and Thunder', + 'releaseYear' => 2000 + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + ]); + + $this->assertEquals(200, $document['headers']['status-code']); + $this->assertEquals('Thor: Love and Thunder', $document['body']['title']); + + $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Thor: Love and Thunder', $document['body']['title']); + + // removing permission to read and delete + $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Love and Thunder', + 'releaseYear' => 2000 + ], + 'permissions' => [ + Permission::update(Role::users()) + ], + ]); + // shouldn't be able to read as no read permission + $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + switch ($this->getSide()) { + case 'client': + $this->assertEquals(404, $document['headers']['status-code']); + break; + case 'server': + $this->assertEquals(200, $document['headers']['status-code']); + break; + } + // shouldn't be able to delete as no delete permission + $document = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + // simulating for the client + // the document should not be allowed to be deleted as needed downward + if ($this->getSide() === 'client') { + $this->assertEquals(401, $document['headers']['status-code']); + } + // giving the delete permission + $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'title' => 'Thor: Love and Thunder', + 'releaseYear' => 2000 + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()) + ], + ]); + $document = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(204, $document['headers']['status-code']); + + // relationship behaviour + $person = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'person-upsert', + 'name' => 'person', + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + Permission::create(Role::users()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $person['headers']['status-code']); + + $library = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'library-upsert', + 'name' => 'library', + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::create(Role::users()), + Permission::delete(Role::users()), + ], + 'documentSecurity' => true, + ]); + + $this->assertEquals(201, $library['headers']['status-code']); + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'fullName', + 'size' => 255, + 'required' => false, + ]); + + sleep(1); // Wait for worker + + $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedtableId' => 'library-upsert', + 'type' => Database::RELATION_ONE_TO_ONE, + 'key' => 'library', + 'twoWay' => true, + 'onDelete' => Database::RELATION_MUTATE_CASCADE, + ]); + + sleep(1); // Wait for worker + + $libraryName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $library['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'libraryName', + 'size' => 255, + 'required' => true, + ]); + + sleep(1); // Wait for worker + + $this->assertEquals(202, $libraryName['headers']['status-code']); + + // upserting values + $rowId = ID::unique(); + $person1 = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.$rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'library' => [ + '$id' => 'library1', + '$permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + 'libraryName' => 'Library 1', + ], + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ] + ]); + + $this->assertEquals('Library 1', $person1['body']['library']['libraryName']); + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['fullName', 'library.*'])->toString(), + Query::equal('library', ['library1'])->toString(), + ], + ]); + + $this->assertEquals(1, $documents['body']['total']); + $this->assertEquals('Library 1', $documents['body']['documents'][0]['library']['libraryName']); + + + $person1 = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.$rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'library' => [ + '$id' => 'library1', + '$permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + 'libraryName' => 'Library 2', + ], + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ] + ]); + + // data should get updated + $this->assertEquals('Library 2', $person1['body']['library']['libraryName']); + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['fullName', 'library.*'])->toString(), + Query::equal('library', ['library1'])->toString(), + ], + ]); + + $this->assertEquals(1, $documents['body']['total']); + $this->assertEquals('Library 2', $documents['body']['documents'][0]['library']['libraryName']); + + + // data should get added + $person1 = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.ID::unique(), array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'library' => [ + '$id' => 'library2', + '$permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ], + 'libraryName' => 'Library 2', + ], + ], + 'permissions' => [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ] + ]); + + $this->assertEquals('Library 2', $person1['body']['library']['libraryName']); + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['fullName', 'library.*'])->toString() + ], + ]); + $this->assertEquals(2, $documents['body']['total']); + } + + /** + * @depends testCreateRow + */ + public function testListRows(array $data): array { $databaseId = $data['databaseId']; $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ @@ -1687,11 +2082,10 @@ trait DatabasesBase return ['rows' => $rows['body']['rows'], 'databaseId' => $databaseId]; } - /** - * @depends testListDocuments + * @depends testListRows */ - public function testGetDocument(array $data): void + public function testGetRow(array $data): void { $databaseId = $data['databaseId']; foreach ($data['rows'] as $row) { @@ -1715,9 +2109,9 @@ trait DatabasesBase } /** - * @depends testListDocuments + * @depends testListRows */ - public function testGetDocumentWithQueries(array $data): void + public function testGetRowWithQueries(array $data): void { $databaseId = $data['databaseId']; $row = $data['rows'][0]; @@ -1738,9 +2132,9 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateRow */ - public function testListDocumentsAfterPagination(array $data): array + public function testListRowsAfterPagination(array $data): array { $databaseId = $data['databaseId']; /** @@ -1880,9 +2274,9 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateRow */ - public function testListDocumentsBeforePagination(array $data): array + public function testListRowsBeforePagination(array $data): array { $databaseId = $data['databaseId']; /** @@ -1993,9 +2387,9 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateRow */ - public function testListDocumentsLimitAndOffset(array $data): array + public function testListRowsLimitAndOffset(array $data): array { $databaseId = $data['databaseId']; $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ @@ -2032,9 +2426,9 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateRow */ - public function testDocumentsListQueries(array $data): array + public function testRowsListQueries(array $data): array { $databaseId = $data['databaseId']; $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ @@ -2277,9 +2671,9 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateRow */ - public function testUpdateDocument(array $data): array + public function testUpdateRow(array $data): array { $databaseId = $data['databaseId']; $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ @@ -2403,9 +2797,9 @@ trait DatabasesBase } /** - * @depends testCreateDocument + * @depends testCreateRow */ - public function testDeleteDocument(array $data): array + public function testDeleteRow(array $data): array { $databaseId = $data['databaseId']; $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ @@ -2454,7 +2848,7 @@ trait DatabasesBase return $data; } - public function testInvalidDocumentStructure() + public function testInvalidRowStructure(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -3005,7 +3399,7 @@ trait DatabasesBase } /** - * @depends testDeleteDocument + * @depends testDeleteRow */ public function testDefaultPermissions(array $data): array { @@ -3124,7 +3518,7 @@ trait DatabasesBase return $data; } - public function testEnforceCollectionAndDocumentPermissions(): void + public function testEnforceTableAndRowPermissions(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -3317,7 +3711,7 @@ trait DatabasesBase $this->assertCount(1, $rowsUser2['body']['rows']); } - public function testEnforceCollectionPermissions() + public function testEnforceTablePermissions(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -3610,7 +4004,7 @@ trait DatabasesBase /** * @depends testUniqueIndexDuplicate */ - public function testPersistantCreatedAt(array $data): array + public function testPersistentCreatedAt(array $data): array { $headers = $this->getSide() === 'client' ? array_merge([ 'content-type' => 'application/json', @@ -3789,7 +4183,7 @@ trait DatabasesBase /** * @depends testCreateDatabase */ - public function testAttributeBooleanDefault(array $data): void + public function testColumnBooleanDefault(array $data): void { $databaseId = $data['databaseId']; @@ -4916,4 +5310,211 @@ trait DatabasesBase $this->assertEquals(408, $response['headers']['status-code']); } + + /** + * @throws \Exception + */ + public function testIncrementColumn(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'CounterDatabase' + ]); + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'CounterCollection', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + $tableId = $collection['body']['$id']; + + // Add integer attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'count', + 'required' => true, + ]); + + \sleep(3); + + // Create document with initial count = 5 + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'count' => 5 + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + $this->assertEquals(201, $doc['headers']['status-code']); + + $docId = $doc['body']['$id']; + + // Increment by default 1 + $inc = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/tables/$tableId/rows/$docId/count/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + $this->assertEquals(200, $inc['headers']['status-code']); + $this->assertEquals(6, $inc['body']['count']); + + // Verify count = 6 + $get = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/tables/$tableId/rows/$docId", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(6, $get['body']['count']); + + // Increment by custom value 4 + $inc2 = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/tables/$tableId/rows/$docId/count/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'value' => 4 + ]); + $this->assertEquals(200, $inc2['headers']['status-code']); + $this->assertEquals(10, $inc2['body']['count']); + + $get2 = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/tables/$tableId/rows/$docId", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(10, $get2['body']['count']); + + // Test max limit exceeded + $err = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/tables/$tableId/rows/$docId/count/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), ['max' => 8]); + $this->assertEquals(400, $err['headers']['status-code']); + + // Test attribute not found + $notFound = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/tables/$tableId/rows/$docId/unknown/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + $this->assertEquals(404, $notFound['headers']['status-code']); + } + + public function testDecrementColumn(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'CounterDatabase' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'CounterCollection', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + $tableId = $collection['body']['$id']; + + // Add integer attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'count', + 'required' => true, + ]); + + \sleep(2); + + // Create document with initial count = 10 + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => ['count' => 10], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $rowId = $doc['body']['$id']; + + // Decrement by default 1 (count = 10 -> 9) + $dec = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + $this->assertEquals(200, $dec['headers']['status-code']); + $this->assertEquals(9, $dec['body']['count']); + + $get = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(9, $get['body']['count']); + + // Decrement by custom value 3 (count 9 -> 6) + $dec2 = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'value' => 3 + ]); + $this->assertEquals(200, $dec2['headers']['status-code']); + $this->assertEquals(6, $dec2['body']['count']); + + $get2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(6, $get2['body']['count']); + + // Test min limit exceeded + $err = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), ['min' => 7]); + $this->assertEquals(400, $err['headers']['status-code']); + + // Test type error on non-numeric attribute + $typeErr = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), ['value' => 'not-a-number']); + $this->assertEquals(400, $typeErr['headers']['status-code']); + } } diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php index a92f1decf1..e2aebf4969 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php @@ -866,13 +866,13 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals($table['body']['indexes'][0]['key'], $index['body']['key']); // Delete attribute - $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/' . $unneededId, array_merge([ + $column = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/' . $unneededId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertEquals(204, $attribute['headers']['status-code']); + $this->assertEquals(204, $column['headers']['status-code']); sleep(2); @@ -938,7 +938,7 @@ class DatabasesCustomServerTest extends Scope public function testDeleteIndexOnDeleteAttribute($data) { $databaseId = $data['databaseId']; - $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ + $column1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -948,7 +948,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ + $column2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -958,10 +958,10 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $attribute1['headers']['status-code']); - $this->assertEquals(202, $attribute2['headers']['status-code']); - $this->assertEquals('attribute1', $attribute1['body']['key']); - $this->assertEquals('attribute2', $attribute2['body']['key']); + $this->assertEquals(202, $column1['headers']['status-code']); + $this->assertEquals(202, $column2['headers']['status-code']); + $this->assertEquals('attribute1', $column1['body']['key']); + $this->assertEquals('attribute2', $column2['body']['key']); sleep(2); @@ -994,7 +994,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); // Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $attribute2['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $column2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1017,10 +1017,10 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals($index1['body']['key'], $table['body']['indexes'][0]['key']); $this->assertIsArray($table['body']['indexes'][0]['columns']); $this->assertCount(1, $table['body']['indexes'][0]['columns']); - $this->assertEquals($attribute1['body']['key'], $table['body']['indexes'][0]['columns'][0]); + $this->assertEquals($column1['body']['key'], $table['body']['indexes'][0]['columns'][0]); // Delete attribute - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $attribute1['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $column1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1066,7 +1066,7 @@ class DatabasesCustomServerTest extends Scope $tableId = $table['body']['$id']; - $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + $column1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1076,7 +1076,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + $column2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1086,10 +1086,10 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $attribute1['headers']['status-code']); - $this->assertEquals(202, $attribute2['headers']['status-code']); - $this->assertEquals('attribute1', $attribute1['body']['key']); - $this->assertEquals('attribute2', $attribute2['body']['key']); + $this->assertEquals(202, $column1['headers']['status-code']); + $this->assertEquals(202, $column2['headers']['status-code']); + $this->assertEquals('attribute1', $column1['body']['key']); + $this->assertEquals('attribute2', $column2['body']['key']); sleep(2); @@ -1122,7 +1122,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); // Expected behavior: deleting attribute1 would cause index1 to be a duplicate of index2 and automatically removed - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $attribute1['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $column1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1145,10 +1145,10 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals($index2['body']['key'], $table['body']['indexes'][0]['key']); $this->assertIsArray($table['body']['indexes'][0]['columns']); $this->assertCount(1, $table['body']['indexes'][0]['columns']); - $this->assertEquals($attribute2['body']['key'], $table['body']['indexes'][0]['columns'][0]); + $this->assertEquals($column2['body']['key'], $table['body']['indexes'][0]['columns'][0]); // Delete attribute - $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $attribute2['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $column2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1336,7 +1336,7 @@ class DatabasesCustomServerTest extends Scope // Add wide string attributes to approach row width limit for ($i = 0; $i < 15; $i++) { - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1346,7 +1346,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); } sleep(5); @@ -1403,7 +1403,7 @@ class DatabasesCustomServerTest extends Scope // add unique attributes for indexing for ($i = 0; $i < 64; $i++) { // $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1413,7 +1413,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); } sleep(10); @@ -1431,8 +1431,8 @@ class DatabasesCustomServerTest extends Scope $this->assertCount(64, $table['body']['columns']); $this->assertCount(0, $table['body']['indexes']); - foreach ($table['body']['columns'] as $attribute) { - $this->assertEquals('available', $attribute['status'], 'attribute: ' . $attribute['key']); + foreach ($table['body']['columns'] as $column) { + $this->assertEquals('available', $column['status'], 'attribute: ' . $column['key']); } // Test indexLimit = 64 @@ -1519,7 +1519,7 @@ class DatabasesCustomServerTest extends Scope /** * Create String Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1529,12 +1529,12 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); /** * Create Email Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1543,12 +1543,12 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); /** * Create IP Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1557,12 +1557,12 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); /** * Create URL Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1571,12 +1571,12 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); /** * Create Integer Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1585,12 +1585,12 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); /** * Create Float Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1602,7 +1602,7 @@ class DatabasesCustomServerTest extends Scope /** * Create Boolean Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1614,7 +1614,7 @@ class DatabasesCustomServerTest extends Scope /** * Create Datetime Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1626,7 +1626,7 @@ class DatabasesCustomServerTest extends Scope /** * Create Enum Attribute */ - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ + $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1636,7 +1636,7 @@ class DatabasesCustomServerTest extends Scope 'elements' => ['lorem', 'ipsum'] ]); - $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals(202, $column['headers']['status-code']); sleep(5); @@ -1681,10 +1681,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals('lorem', $attribute['default']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals('lorem', $column['default']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ 'content-type' => 'application/json', @@ -1823,10 +1823,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals('torsten@appwrite.io', $attribute['default']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals('torsten@appwrite.io', $column['default']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email/' . $key, array_merge([ @@ -1966,10 +1966,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals('127.0.0.1', $attribute['default']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals('127.0.0.1', $column['default']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2108,10 +2108,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals('http://appwrite.io', $attribute['default']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals('http://appwrite.io', $column['default']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2254,12 +2254,12 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals(123, $attribute['default']); - $this->assertEquals(0, $attribute['min']); - $this->assertEquals(1000, $attribute['max']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals(123, $column['default']); + $this->assertEquals(0, $column['min']); + $this->assertEquals(1000, $column['max']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2515,12 +2515,12 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals(123.456, $attribute['default']); - $this->assertEquals(0, $attribute['min']); - $this->assertEquals(1000, $attribute['max']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals(123.456, $column['default']); + $this->assertEquals(0, $column['min']); + $this->assertEquals(1000, $column['max']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2772,10 +2772,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals(true, $attribute['default']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals(true, $column['default']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean/' . $key, array_merge([ 'content-type' => 'application/json', @@ -2914,10 +2914,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals('1975-06-12 14:12:55+02:00', $attribute['default']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals('1975-06-12 14:12:55+02:00', $column['default']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3061,14 +3061,14 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $attribute = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; - $this->assertNotNull($attribute); - $this->assertFalse($attribute['required']); - $this->assertEquals('lorem', $attribute['default']); - $this->assertCount(3, $attribute['elements']); - $this->assertContains('lorem', $attribute['elements']); - $this->assertContains('ipsum', $attribute['elements']); - $this->assertContains('dolor', $attribute['elements']); + $column = array_values(array_filter($new['body']['columns'], fn (array $a) => $a['key'] === $key))[0] ?? null; + $this->assertNotNull($column); + $this->assertFalse($column['required']); + $this->assertEquals('lorem', $column['default']); + $this->assertCount(3, $column['elements']); + $this->assertContains('lorem', $column['elements']); + $this->assertContains('ipsum', $column['elements']); + $this->assertContains('dolor', $column['elements']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum/' . $key, array_merge([ 'content-type' => 'application/json', @@ -3264,7 +3264,7 @@ class DatabasesCustomServerTest extends Scope ); // Test Resize Up - $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + $column = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3274,8 +3274,8 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(200, $attribute['headers']['status-code']); - $this->assertEquals(2048, $attribute['body']['size']); + $this->assertEquals(200, $column['headers']['status-code']); + $this->assertEquals(2048, $column['body']['size']); // Test create new document with new size $newDoc = $this->client->call( @@ -3313,7 +3313,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(2048, strlen($row['body']['string'])); // Test Exception on resize down with data that is too large - $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + $column = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3323,8 +3323,8 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(400, $attribute['headers']['status-code']); - $this->assertEquals(AppwriteException::COLUMN_INVALID_RESIZE, $attribute['body']['type']); + $this->assertEquals(400, $column['headers']['status-code']); + $this->assertEquals(AppwriteException::COLUMN_INVALID_RESIZE, $column['body']['type']); // original documents to original size, remove new document $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ @@ -3350,7 +3350,7 @@ class DatabasesCustomServerTest extends Scope // Test Resize Down - $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ + $column = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string/' . $key, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3360,8 +3360,8 @@ class DatabasesCustomServerTest extends Scope 'required' => false ]); - $this->assertEquals(200, $attribute['headers']['status-code']); - $this->assertEquals(10, $attribute['body']['size']); + $this->assertEquals(200, $column['headers']['status-code']); + $this->assertEquals(10, $column['body']['size']); // Test create new document with new size $newDoc = $this->client->call( @@ -3611,7 +3611,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $doc3['headers']['status-code']); } - public function createRelationshipCollections() + public function createRelationshipCollections(): void { // Prepare the database with collections and relationships $database = $this->client->call(Client::METHOD_POST, '/databases', [ @@ -3660,7 +3660,7 @@ class DatabasesCustomServerTest extends Scope \sleep(2); } - public function cleanupRelationshipCollection() + public function cleanupRelationshipCollection(): void { $this->client->call(Client::METHOD_DELETE, '/databases/database1', [ 'content-type' => 'application/json', @@ -4110,4 +4110,1043 @@ class DatabasesCustomServerTest extends Scope $this->cleanupRelationshipCollection(); } + + public function testBulkCreate(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Bulk Create Perms', + ]); + + $this->assertNotEmpty($database['body']['$id']); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Create Perms', + 'rowSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $data = [ + '$id' => $collection['body']['$id'], + 'databaseId' => $collection['body']['databaseId'] + ]; + + // Await attribute + $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'number', + 'required' => true, + ]); + + $this->assertEquals(202, $numberAttribute['headers']['status-code']); + + sleep(1); + + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + '$id' => ID::unique(), + 'number' => 1, + ], + [ + '$id' => ID::unique(), + 'number' => 2, + ], + [ + '$id' => ID::unique(), + 'number' => 3, + ], + ], + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertCount(3, $response['body']['rows']); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(1, $response['body']['rows'][0]['number']); + $this->assertEquals(2, $response['body']['rows'][1]['number']); + $this->assertEquals(3, $response['body']['rows'][2]['number']); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(3, $response['body']['rows']); + + // TEST SUCCESS - $id is auto-assigned if not included in bulk documents + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + 'number' => 1, + ] + ], + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // TEST FAIL - Can't use data and document together + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 5 + ], + 'rows' => [ + [ + '$id' => ID::unique(), + 'number' => 1, + ] + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // TEST FAIL - Can't use $rowId and create bulk documents + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'rows' => [ + [ + '$id' => ID::unique(), + 'number' => 1, + ] + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // TEST FAIL - Can't include invalid ID in bulk documents + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + '$id' => '$invalid', + 'number' => 1, + ] + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // TEST FAIL - Can't miss number in bulk documents + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + '$id' => ID::unique(), + 'number' => 1, + ], + [ + '$id' => ID::unique(), + ], + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // TEST FAIL - Can't push more than APP_LIMIT_DATABASE_BATCH documents + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => array_fill(0, APP_LIMIT_DATABASE_BATCH + 1, [ + '$id' => ID::unique(), + 'number' => 1, + ]), + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // TEST FAIL - Can't include invalid permissions in nested documents + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + '$id' => ID::unique(), + '$permissions' => ['invalid'], + 'number' => 1, + ], + ], + ]); + + // TEST FAIL - Can't bulk create in a collection with relationships + $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Related', + 'rowSecurity' => true, + 'permissions' => [], + ]); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders()), [ + 'relatedTableId' => $collection2['body']['$id'], + 'type' => 'manyToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + $this->assertEquals(202, $response['headers']['status-code']); + + sleep(1); + + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + ['$id' => ID::unique(), 'number' => 1,], + ['$id' => ID::unique(), 'number' => 2,], + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testBulkUpdate(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Bulk Updates' + ]); + + $this->assertNotEmpty($database['body']['$id']); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Updates', + 'rowSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $data = [ + '$id' => $collection['body']['$id'], + 'databaseId' => $collection['body']['databaseId'] + ]; + + // Await attribute + $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'number', + 'required' => true, + ]); + + $this->assertEquals(202, $numberAttribute['headers']['status-code']); + + // Wait for database worker to create attributes + sleep(2); + + // Create documents + $createBulkDocuments = function ($amount = 10) use ($data) { + $documents = []; + + for ($x = 1; $x <= $amount; $x++) { + $documents[] = [ + '$id' => ID::unique(), + 'number' => $x, + ]; + } + + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => $documents, + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + }; + + $createBulkDocuments(); + + // TEST: Update all documents + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 100, + '$permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(10, $response['body']['rows']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + Query::equal('number', [100])->toString(), + ]); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(10, $documents['body']['total']); + + $returnedDocuments = $response['body']['rows']; + $refetchedDocuments = $documents['body']['rows']; + + $this->assertEquals($returnedDocuments, $refetchedDocuments); + + foreach ($documents['body']['rows'] as $document) { + $this->assertEquals([ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], $document['$permissions']); + $this->assertEquals($collection['body']['$id'], $document['$tableId']); + $this->assertEquals($data['databaseId'], $document['$databaseId']); + $this->assertEquals(100, $document['number']); + } + + // TEST: Check permissions persist + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 200 + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(10, $response['body']['rows']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + Query::equal('number', [200])->toString(), + ]); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(10, $documents['body']['total']); + + foreach ($documents['body']['rows'] as $document) { + $this->assertEquals([ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], $document['$permissions']); + } + + // TEST: Update documents with limit + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 300 + ], + 'queries' => [ + Query::limit(5)->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(5, $response['body']['rows']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('number', [200])->toString()] + ]); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(5, $documents['body']['total']); + + // TEST: Update documents with offset + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 300 + ], + 'queries' => [ + Query::offset(5)->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(5, $response['body']['rows']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('number', [300])->toString()] + ]); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(10, $documents['body']['total']); + + // TEST: Update documents with equals filter + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 400 + ], + 'queries' => [ + Query::equal('number', [300])->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(10, $response['body']['rows']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('number', [400])->toString()] + ]); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(10, $documents['body']['total']); + + // TEST: Fail - Can't bulk update in a collection with relationships + $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Related', + 'rowSecurity' => true, + 'permissions' => [], + ]); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders()), [ + 'relatedTableId' => $collection2['body']['$id'], + 'type' => 'manyToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + $this->assertEquals(202, $response['headers']['status-code']); + + sleep(1); + + $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'number' => 500 + ], + 'queries' => [ + Query::equal('number', [300])->toString(), + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testBulkUpsert(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Bulk Upserts' + ]); + + $this->assertNotEmpty($database['body']['$id']); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Upserts', + 'rowSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $data = [ + '$id' => $collection['body']['$id'], + 'databaseId' => $collection['body']['databaseId'] + ]; + + // Await attribute + $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'number', + 'required' => true, + ]); + + $this->assertEquals(202, $numberAttribute['headers']['status-code']); + + // Wait for database worker to create attributes + sleep(2); + + // Create documents + $createBulkDocuments = function ($amount = 10) use ($data) { + $documents = []; + + for ($x = 1; $x <= $amount; $x++) { + $documents[] = [ + '$id' => "$x", + 'number' => $x, + ]; + } + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => $documents, + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + return $documents; + }; + + $documents = $createBulkDocuments(); + + // Update 1 document + $documents[\array_key_last($documents)]['number'] = 1000; + + // Add 1 document + $documents[] = ['number' => 11]; + + // TEST: Upsert all documents + $response = $this->client->call(Client::METHOD_PUT, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => $documents, + ]); + + // Unchanged docs are skipped. 2 documents should be returned, 1 updated and 1 inserted. + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertCount(2, $response['body']['rows']); + $this->assertEquals(1000, $response['body']['rows'][0]['number']); + $this->assertEquals(11, $response['body']['rows'][1]['number']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(11, $documents['body']['total']); + + foreach ($documents['body']['rows'] as $index => $document) { + $this->assertEquals($collection['body']['$id'], $document['$tableId']); + $this->assertEquals($data['databaseId'], $document['$databaseId']); + switch ($index) { + case 9: + $this->assertEquals(1000, $document['number']); + break; + default: + $this->assertEquals($index + 1, $document['number']); + } + } + + // TEST: Upsert permissions + $response = $this->client->call(Client::METHOD_PUT, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + '$id' => '1', + 'number' => 1000, + ], + [ + '$id' => '10', + '$permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], + 'number' => 10, + ], + ], + ]); + + $this->assertEquals(1000, $response['body']['rows'][0]['number']); + $this->assertEquals([], $response['body']['rows'][0]['$permissions']); + $this->assertEquals([ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ], $response['body']['rows'][1]['$permissions']); + + // TEST: Fail - Can't bulk upsert in a collection with relationships + $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Related', + 'rowSecurity' => true, + 'permissions' => [], + ]); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders()), [ + 'relatedTableId' => $collection2['body']['$id'], + 'type' => 'manyToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + $this->assertEquals(202, $response['headers']['status-code']); + + sleep(1); + + $response = $this->client->call(Client::METHOD_PUT, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => [ + [ + '$id' => '1', + 'number' => 1000, + ], + ], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testBulkDelete(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Bulk Deletes' + ]); + + $this->assertNotEmpty($database['body']['$id']); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Deletes', + 'rowSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $data = [ + '$id' => $collection['body']['$id'], + 'databaseId' => $collection['body']['databaseId'] + ]; + + // Await attribute + $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'number', + 'required' => true, + ]); + + $this->assertEquals(202, $numberAttribute['headers']['status-code']); + + // wait for database worker to create attributes + sleep(2); + + // Create documents + $createBulkDocuments = function ($amount = 11) use ($data) { + $documents = []; + + for ($x = 0; $x < $amount; $x++) { + $documents[] = [ + '$id' => ID::unique(), + 'number' => $x, + ]; + } + + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rows' => $documents, + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + }; + + $createBulkDocuments(); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(11, $documents['body']['total']); + + // TEST: Delete all documents + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(11, $response['body']['total']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(0, $documents['body']['total']); + + // TEST: Delete documents with query + $createBulkDocuments(); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(11, $documents['body']['total']); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::lessThan('number', 5)->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(5, $response['body']['total']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(6, $documents['body']['total']); + + foreach ($documents['body']['rows'] as $document) { + $this->assertGreaterThanOrEqual(5, $document['number']); + } + + // Cleanup + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(6, $response['body']['total']); + + // SUCCESS: Delete documents with query + $createBulkDocuments(); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(11, $documents['body']['total']); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::lessThan('number', 5)->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(5, $response['body']['total']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(6, $documents['body']['total']); + + // Cleanup + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(6, $response['body']['total']); + + // SUCCESS: Delete Documents with limit query + $createBulkDocuments(); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(11, $documents['body']['total']); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::limit(2)->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['total']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(9, $documents['body']['total']); + + // Cleanup + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(9, $response['body']['total']); + + // SUCCESS: Delete Documents with offset query + $createBulkDocuments(); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(11, $documents['body']['total']); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::offset(5)->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(6, $response['body']['total']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(5, $documents['body']['total']); + + $lastDoc = end($documents['body']['rows']); + + $this->assertNotEmpty($lastDoc); + $this->assertEquals(4, $lastDoc['number']); + + // Cleanup + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(5, $response['body']['total']); + + // SUCCESS: Delete 100 documents + $createBulkDocuments(100); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(100, $documents['body']['total']); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(100, $response['body']['total']); + + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(0, $documents['body']['total']); + + // TEST: Fail - Can't bulk delete in a collection with relationships + $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Bulk Related', + 'rowSecurity' => true, + 'permissions' => [], + ]); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/relationship', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders()), [ + 'relatedTableId' => $collection2['body']['$id'], + 'type' => 'manyToOne', + 'twoWay' => true, + 'onDelete' => 'cascade', + 'key' => 'level2', + 'twoWayKey' => 'level1' + ]); + + $this->assertEquals(202, $response['headers']['status-code']); + + sleep(1); + + $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(400, $response['headers']['status-code']); + } } From 58e878653e105c724415d3dd2f04000a46d05f00 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 20:00:53 +0530 Subject: [PATCH 139/173] =?UTF-8?q?fix:=20one=20more=20test=20before=20I?= =?UTF-8?q?=20sign=20out=20=F0=9F=91=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modules/Databases/Http/Databases/Tables/Rows/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 81d90a7708..5af7fe5dc2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -98,7 +98,7 @@ class Create extends DocumentCreate ) ]) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.') ->param('data', [], new JSON(), 'Row data as JSON object.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) From 018aad5339e73559ac78e9f2604a135c4bd048af Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 12 Jun 2025 20:03:41 +0530 Subject: [PATCH 140/173] fix: one more test... --- app/controllers/api/databases.php | 0 .../Modules/Databases/Http/Databases/Tables/Rows/Create.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 app/controllers/api/databases.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 5af7fe5dc2..881d2b28ed 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -100,7 +100,7 @@ class Create extends DocumentCreate ->param('databaseId', '', new UID(), 'Database ID.') ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.') - ->param('data', [], new JSON(), 'Row data as JSON object.') + ->param('data', [], new JSON(), 'Row data as JSON object.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) ->inject('response') From 5f8c811f15837f4db680f2813c9fc16997f86672 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 10:08:07 +0530 Subject: [PATCH 141/173] fix: missing context. --- .../Databases/Http/Databases/Tables/Rows/Bulk/Update.php | 3 +++ .../Databases/Http/Databases/Tables/Rows/Column/Decrement.php | 3 +++ .../Databases/Http/Databases/Tables/Rows/Column/Increment.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php index 53e7c3c152..1b9ddb60ab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Update as DocumentsUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -31,6 +32,8 @@ class Update extends DocumentsUpdate public function __construct() { + $this->setContext(Context::DATABASE_ROWS); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php index 5b2cf17f91..128d578cde 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Column; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute\Decrement as DecrementDocumentAttribute; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -30,6 +31,8 @@ class Decrement extends DecrementDocumentAttribute public function __construct() { + $this->setContext(Context::DATABASE_ROWS); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/:column/decrement') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php index 4dcd05cc25..a3e256d561 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Column; +use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute\Increment as IncrementDocumentAttribute; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -30,6 +31,8 @@ class Increment extends IncrementDocumentAttribute public function __construct() { + $this->setContext(Context::DATABASE_ROWS); + $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/:column/increment') From 44870b9810b3d7c2a63e58d710b099ff3c0f44c0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 10:26:05 +0530 Subject: [PATCH 142/173] fix: pending tests. --- .github/workflows/tests.yml | 6 +- .../Databases/Tables/DatabasesBase.php | 154 +++++++++--------- .../Tables/DatabasesPermissionsGuestTest.php | 84 +++++----- .../Tables/DatabasesPermissionsMemberTest.php | 10 +- .../Tables/DatabasesPermissionsTeamTest.php | 62 +++---- 5 files changed, 159 insertions(+), 157 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 97f3696e67..cf1e55f7ec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,7 +145,8 @@ jobs: Account, Avatars, Console, - Databases, + Databases/Collections, + Databases/Tables, Functions, FunctionsSchedule, GraphQL, @@ -213,7 +214,8 @@ jobs: Account, Avatars, Console, - Databases, + Databases/Collections, + Databases/Tables, Functions, FunctionsSchedule, GraphQL, diff --git a/tests/e2e/Services/Databases/Tables/DatabasesBase.php b/tests/e2e/Services/Databases/Tables/DatabasesBase.php index a0b6be2fbe..12426d78b2 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesBase.php @@ -1437,7 +1437,7 @@ trait DatabasesBase ], [ 'key' => 'lengthTestIndex', 'type' => 'key', - 'attributes' => ['title','description'], + 'columns' => ['title','description'], 'lengths' => [128,200] ]); $this->assertEquals(202, $create['headers']['status-code']); @@ -1461,7 +1461,7 @@ trait DatabasesBase ], [ 'key' => 'lengthOverrideTestIndex', 'type' => 'key', - 'attributes' => ['actors'], + 'columns' => ['actors'], 'lengths' => [120] ]); $this->assertEquals(202, $create['headers']['status-code']); @@ -1481,7 +1481,7 @@ trait DatabasesBase ], [ 'key' => 'lengthCountExceededIndex', 'type' => 'key', - 'attributes' => ['title'], + 'columns' => ['title'], 'lengths' => [128, 128] ]); $this->assertEquals(400, $create['headers']['status-code']); @@ -1494,7 +1494,7 @@ trait DatabasesBase ], [ 'key' => 'lengthTooLargeIndex', 'type' => 'key', - 'attributes' => ['title','description','tagline','actors'], + 'columns' => ['title','description','tagline','actors'], 'lengths' => [256,256,256,20] ]); @@ -1508,7 +1508,7 @@ trait DatabasesBase ], [ 'key' => 'negativeLengthIndex', 'type' => 'key', - 'attributes' => ['title'], + 'columns' => ['title'], 'lengths' => [-1] ]); $this->assertEquals(400, $create['headers']['status-code']); @@ -1679,12 +1679,6 @@ trait DatabasesBase $this->assertEquals(400, $row4['headers']['status-code']); - // Delete document 4 with incomplete path - $this->assertEquals(404, $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()))['headers']['status-code']); - return $data; } @@ -1695,7 +1689,7 @@ trait DatabasesBase { $databaseId = $data['databaseId']; $rowId = ID::unique(); - $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1710,16 +1704,16 @@ trait DatabasesBase ], ]); - $this->assertEquals(200, $document['headers']['status-code']); - $this->assertCount(3, $document['body']['$permissions']); - $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertCount(3, $row['body']['$permissions']); + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals('Thor: Ragnarok', $document['body']['title']); + $this->assertEquals('Thor: Ragnarok', $row['body']['title']); - $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1734,18 +1728,18 @@ trait DatabasesBase ], ]); - $this->assertEquals(200, $document['headers']['status-code']); - $this->assertEquals('Thor: Love and Thunder', $document['body']['title']); + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals('Thor: Love and Thunder', $row['body']['title']); - $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals('Thor: Love and Thunder', $document['body']['title']); + $this->assertEquals('Thor: Love and Thunder', $row['body']['title']); // removing permission to read and delete - $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1758,30 +1752,30 @@ trait DatabasesBase ], ]); // shouldn't be able to read as no read permission - $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); switch ($this->getSide()) { case 'client': - $this->assertEquals(404, $document['headers']['status-code']); + $this->assertEquals(404, $row['headers']['status-code']); break; case 'server': - $this->assertEquals(200, $document['headers']['status-code']); + $this->assertEquals(200, $row['headers']['status-code']); break; } // shouldn't be able to delete as no delete permission - $document = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); // simulating for the client - // the document should not be allowed to be deleted as needed downward + // the row should not be allowed to be deleted as needed downward if ($this->getSide() === 'client') { - $this->assertEquals(401, $document['headers']['status-code']); + $this->assertEquals(401, $row['headers']['status-code']); } // giving the delete permission - $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + $row = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1795,11 +1789,12 @@ trait DatabasesBase Permission::delete(Role::users()) ], ]); - $document = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ + + $row = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(204, $document['headers']['status-code']); + $this->assertEquals(204, $row['headers']['status-code']); // relationship behaviour $person = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ @@ -1815,7 +1810,7 @@ trait DatabasesBase Permission::delete(Role::users()), Permission::create(Role::users()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $person['headers']['status-code']); @@ -1833,7 +1828,7 @@ trait DatabasesBase Permission::create(Role::users()), Permission::delete(Role::users()), ], - 'documentSecurity' => true, + 'rowSecurity' => true, ]); $this->assertEquals(201, $library['headers']['status-code']); @@ -1855,7 +1850,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'relatedtableId' => 'library-upsert', + 'relatedTableId' => 'library-upsert', 'type' => Database::RELATION_ONE_TO_ONE, 'key' => 'library', 'twoWay' => true, @@ -1903,7 +1898,7 @@ trait DatabasesBase ]); $this->assertEquals('Library 1', $person1['body']['library']['libraryName']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/documents', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1913,8 +1908,8 @@ trait DatabasesBase ], ]); - $this->assertEquals(1, $documents['body']['total']); - $this->assertEquals('Library 1', $documents['body']['documents'][0]['library']['libraryName']); + $this->assertEquals(1, $rows['body']['total']); + $this->assertEquals('Library 1', $rows['body']['rows'][0]['library']['libraryName']); $person1 = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.$rowId, array_merge([ @@ -1941,7 +1936,7 @@ trait DatabasesBase // data should get updated $this->assertEquals('Library 2', $person1['body']['library']['libraryName']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/documents', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1951,9 +1946,8 @@ trait DatabasesBase ], ]); - $this->assertEquals(1, $documents['body']['total']); - $this->assertEquals('Library 2', $documents['body']['documents'][0]['library']['libraryName']); - + $this->assertEquals(1, $rows['body']['total']); + $this->assertEquals('Library 2', $rows['body']['rows'][0]['library']['libraryName']); // data should get added $person1 = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.ID::unique(), array_merge([ @@ -1979,7 +1973,8 @@ trait DatabasesBase ]); $this->assertEquals('Library 2', $person1['body']['library']['libraryName']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/documents', array_merge([ + + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1987,7 +1982,8 @@ trait DatabasesBase Query::select(['fullName', 'library.*'])->toString() ], ]); - $this->assertEquals(2, $documents['body']['total']); + + $this->assertEquals(2, $rows['body']['total']); } /** @@ -2044,6 +2040,7 @@ trait DatabasesBase 'default' => null, 'required' => false, ]); + // creating a dummy doc with null description $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -2242,7 +2239,7 @@ trait DatabasesBase $this->assertCount(1, $rows['body']['rows']); /** - * Test after with unknown document. + * Test after with unknown row. */ $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -3066,6 +3063,7 @@ trait DatabasesBase $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']); $this->assertEquals('Cannot set default value for array columns', $defaultArray['body']['message']); $this->assertEquals(400, $datetimeDefault['headers']['status-code']); + // wait for worker to add attributes sleep(3); @@ -3073,7 +3071,7 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), []); + ])); $this->assertCount(10, $table['body']['columns']); @@ -3387,6 +3385,8 @@ trait DatabasesBase $this->assertEquals(400, $tooHigh['headers']['status-code']); $this->assertEquals(400, $tooLow['headers']['status-code']); $this->assertEquals(400, $badTime['headers']['status-code']); + + // TODO: @itznotabug - database library needs to throw error based on context! $this->assertEquals('Invalid document structure: Attribute "email" has invalid format. Value must be a valid email address', $badEmail['body']['message']); $this->assertEquals('Invalid document structure: Attribute "enum" has invalid format. Value must be one of (yes, no, maybe)', $badEnum['body']['message']); $this->assertEquals('Invalid document structure: Attribute "ip" has invalid format. Value must be a valid IP address', $badIp['body']['message']); @@ -3457,7 +3457,7 @@ trait DatabasesBase $this->assertEquals($row['body']['releaseYear'], 1945); // This differs from the old permissions model because we don't inherit - // existing document permissions on update, unless none were supplied, + // existing row permissions on update, unless none were supplied, // so that specific types can be removed if wanted. $this->assertCount(2, $row['body']['$permissions']); $this->assertEquals([ @@ -3500,7 +3500,7 @@ trait DatabasesBase $this->assertCount(0, $row['body']['$permissions']); $this->assertEquals([], $row['body']['$permissions']); - // Check client side can no longer read the document. + // Check client side can no longer read the row. $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3644,7 +3644,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - // Current user has read permission on the collection so can get any document + // Current user has read permission on the table so can get any row $this->assertEquals(3, $rowsUser1['body']['total']); $this->assertCount(3, $rowsUser1['body']['rows']); @@ -3653,7 +3653,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - // Current user has read permission on the collection so can get any document + // Current user has read permission on the table so can get any row $this->assertEquals(200, $row3GetWithCollectionRead['headers']['status-code']); $email = uniqid() . 'user@localhost.test'; @@ -3686,7 +3686,7 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // Current user has no collection permissions but has read permission for this document + // Current user has no table permissions but has read permission for this row $this->assertEquals(200, $row3GetWithDocumentRead['headers']['status-code']); $row2GetFailure = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row2['body']['$id'], [ @@ -3696,7 +3696,7 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // Current user has no collection or document permissions for this document + // Current user has no table or row permissions for this row $this->assertEquals(404, $row2GetFailure['headers']['status-code']); $rowsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ @@ -3706,7 +3706,7 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // Current user has no collection permissions but has read permission for one document + // Current user has no table permissions but has read permission for one row $this->assertEquals(1, $rowsUser2['body']['total']); $this->assertCount(1, $rowsUser2['body']['rows']); } @@ -3832,7 +3832,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - // Current user has read permission on the collection so can get any document + // Current user has read permission on the table so can get any row $this->assertEquals(3, $rowsUser1['body']['total']); $this->assertCount(3, $rowsUser1['body']['rows']); @@ -3841,7 +3841,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - // Current user has read permission on the collection so can get any document + // Current user has read permission on the table so can get any row $this->assertEquals(200, $row3GetWithCollectionRead['headers']['status-code']); $email = uniqid() . 'user2@localhost.test'; @@ -3874,7 +3874,7 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // other2 has no collection permissions and document permissions are disabled + // other2 has no table permissions and row permissions are disabled $this->assertEquals(404, $row3GetWithDocumentRead['headers']['status-code']); $rowsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', [ @@ -3884,11 +3884,11 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // other2 has no collection permissions and document permissions are disabled + // other2 has no table permissions and row permissions are disabled $this->assertEquals(401, $rowsUser2['headers']['status-code']); - // Enable document permissions - $table = $this->client->call(CLient::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $tableId, [ + // Enable row permissions + $this->client->call(CLient::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $tableId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3904,7 +3904,7 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // Current user has no collection permissions read access to one document + // Current user has no table permissions read access to one row $this->assertEquals(1, $rowsUser2['body']['total']); $this->assertCount(1, $rowsUser2['body']['rows']); } @@ -3952,7 +3952,7 @@ trait DatabasesBase $this->assertEquals(409, $duplicate['headers']['status-code']); - // Test for exception when updating document to conflict + // Test for exception when updating row to conflict $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3975,7 +3975,7 @@ trait DatabasesBase $this->assertEquals(201, $row['headers']['status-code']); - // Test for exception when updating document to conflict + // Test for exception when updating row to conflict $duplicate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $row['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4078,7 +4078,7 @@ trait DatabasesBase $databaseId = $database['body']['$id']; - // Create collection + // Create table $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4116,7 +4116,7 @@ trait DatabasesBase // wait for database worker to create attributes sleep(2); - // add document + // add row $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $moviesId . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4171,7 +4171,7 @@ trait DatabasesBase $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']); } - // remove collection + // remove table $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $moviesId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4624,7 +4624,7 @@ trait DatabasesBase { $databaseId = $data['databaseId']; - // Create album collection + // Create album table $albums = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4650,7 +4650,7 @@ trait DatabasesBase 'required' => true, ]); - // Create artist collection + // Create artist table $artists = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4768,7 +4768,7 @@ trait DatabasesBase { $databaseId = $data['databaseId']; - // Create sports collection + // Create sports table $sports = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4794,7 +4794,7 @@ trait DatabasesBase 'required' => true, ]); - // Create player collection + // Create player table $players = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -5326,20 +5326,20 @@ trait DatabasesBase ]); $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'tableId' => ID::unique(), 'name' => 'CounterCollection', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), ], ]); - $tableId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add integer attribute $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ @@ -5353,7 +5353,7 @@ trait DatabasesBase \sleep(3); - // Create document with initial count = 5 + // Create row with initial count = 5 $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -5430,21 +5430,21 @@ trait DatabasesBase $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'tableId' => ID::unique(), 'name' => 'CounterCollection', - 'documentSecurity' => true, + 'rowSecurity' => true, 'permissions' => [ Permission::create(Role::user($this->getUser()['$id'])), Permission::read(Role::user($this->getUser()['$id'])), ], ]); - $tableId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add integer attribute $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ @@ -5458,8 +5458,8 @@ trait DatabasesBase \sleep(2); - // Create document with initial count = 10 - $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/documents', array_merge([ + // Create row with initial count = 10 + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php index fde6041219..f404cc1bd5 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php @@ -17,7 +17,7 @@ class DatabasesPermissionsGuestTest extends Scope use SideClient; use DatabasesPermissionsScope; - public function createCollection(): array + public function createTable(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -25,10 +25,10 @@ class DatabasesPermissionsGuestTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'InvalidDocumentDatabase', + 'name' => 'InvalidRowDatabase', ]); $this->assertEquals(201, $database['headers']['status-code']); - $this->assertEquals('InvalidDocumentDatabase', $database['body']['name']); + $this->assertEquals('InvalidRowDatabase', $database['body']['name']); $databaseId = $database['body']['$id']; $publicMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ @@ -48,15 +48,15 @@ class DatabasesPermissionsGuestTest extends Scope 'rowSecurity' => true, ]); - $publicCollection = ['id' => $publicMovies['body']['$id']]; - $privateCollection = ['id' => $privateMovies['body']['$id']]; + $publicTable = ['id' => $publicMovies['body']['$id']]; + $privateTable = ['id' => $privateMovies['body']['$id']]; - $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicCollection['id'] . '/columns/string', $this->getServerHeader(), [ + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicTable['id'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, ]); - $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollection['id'] . '/columns/string', $this->getServerHeader(), [ + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateTable['id'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, @@ -66,8 +66,8 @@ class DatabasesPermissionsGuestTest extends Scope return [ 'databaseId' => $databaseId, - 'publicCollectionId' => $publicCollection['id'], - 'privateCollectionId' => $privateCollection['id'], + 'publicTableId' => $publicTable['id'], + 'privateTableId' => $privateTable['id'], ]; } @@ -86,21 +86,21 @@ class DatabasesPermissionsGuestTest extends Scope /** * @dataProvider permissionsProvider */ - public function testReadDocuments($permissions) + public function testReadRows($permissions) { - $data = $this->createCollection(); - $publicCollectionId = $data['publicCollectionId']; - $privateCollectionId = $data['privateCollectionId']; + $data = $this->createTable(); + $publicTableId = $data['publicTableId']; + $privateTableId = $data['privateTableId']; $databaseId = $data['databaseId']; - $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows', $this->getServerHeader(), [ + $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicTableId . '/rows', $this->getServerHeader(), [ 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', ], 'permissions' => $permissions, ]); - $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', $this->getServerHeader(), [ + $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateTableId . '/rows', $this->getServerHeader(), [ 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', @@ -114,23 +114,23 @@ class DatabasesPermissionsGuestTest extends Scope $roles = Authorization::getRoles(); Authorization::cleanRoles(); - $publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows', [ + $publicRows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $publicTableId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]); - $privateDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', [ + $privateRows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $privateTableId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]); - $this->assertEquals(1, $publicDocuments['body']['total']); - $this->assertEquals($permissions, $publicDocuments['body']['rows'][0]['$permissions']); + $this->assertEquals(1, $publicRows['body']['total']); + $this->assertEquals($permissions, $publicRows['body']['rows'][0]['$permissions']); if (\in_array(Permission::read(Role::any()), $permissions)) { - $this->assertEquals(1, $privateDocuments['body']['total']); - $this->assertEquals($permissions, $privateDocuments['body']['rows'][0]['$permissions']); + $this->assertEquals(1, $privateRows['body']['total']); + $this->assertEquals($permissions, $privateRows['body']['rows'][0]['$permissions']); } else { - $this->assertEquals(0, $privateDocuments['body']['total']); + $this->assertEquals(0, $privateRows['body']['total']); } foreach ($roles as $role) { @@ -138,17 +138,17 @@ class DatabasesPermissionsGuestTest extends Scope } } - public function testWriteDocument() + public function testWriteRow() { - $data = $this->createCollection(); - $publicCollectionId = $data['publicCollectionId']; - $privateCollectionId = $data['privateCollectionId']; + $data = $this->createTable(); + $publicTableId = $data['publicTableId']; + $privateTableId = $data['privateTableId']; $databaseId = $data['databaseId']; $roles = Authorization::getRoles(); Authorization::cleanRoles(); - $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows', [ + $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $publicTableId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ @@ -158,10 +158,10 @@ class DatabasesPermissionsGuestTest extends Scope ] ]); - $publicDocumentId = $publicResponse['body']['$id']; + $publicRowId = $publicResponse['body']['$id']; $this->assertEquals(201, $publicResponse['headers']['status-code']); - $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', [ + $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateTableId . '/rows', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ @@ -173,8 +173,8 @@ class DatabasesPermissionsGuestTest extends Scope $this->assertEquals(401, $privateResponse['headers']['status-code']); - // Create a document in private collection with API key so we can test that update and delete are also not allowed - $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows', $this->getServerHeader(), [ + // Create a row in private collection with API key so we can test that update and delete are also not allowed + $privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $privateTableId . '/rows', $this->getServerHeader(), [ 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', @@ -182,9 +182,9 @@ class DatabasesPermissionsGuestTest extends Scope ]); $this->assertEquals(201, $privateResponse['headers']['status-code']); - $privateDocumentId = $privateResponse['body']['$id']; + $privateRowId = $privateResponse['body']['$id']; - $publicDocument = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows/' . $publicDocumentId, [ + $publicRow = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $publicTableId . '/rows/' . $publicRowId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ @@ -193,10 +193,10 @@ class DatabasesPermissionsGuestTest extends Scope ], ]); - $this->assertEquals(200, $publicDocument['headers']['status-code']); - $this->assertEquals('Thor: Ragnarok', $publicDocument['body']['title']); + $this->assertEquals(200, $publicRow['headers']['status-code']); + $this->assertEquals('Thor: Ragnarok', $publicRow['body']['title']); - $privateDocument = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows/' . $privateDocumentId, [ + $privateRow = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $privateTableId . '/rows/' . $privateRowId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], [ @@ -205,28 +205,28 @@ class DatabasesPermissionsGuestTest extends Scope ], ]); - $this->assertEquals(401, $privateDocument['headers']['status-code']); + $this->assertEquals(401, $privateRow['headers']['status-code']); - $publicDocument = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $publicCollectionId . '/rows/' . $publicDocumentId, [ + $publicRow = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $publicTableId . '/rows/' . $publicRowId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]); - $this->assertEquals(204, $publicDocument['headers']['status-code']); + $this->assertEquals(204, $publicRow['headers']['status-code']); - $privateDocument = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $privateCollectionId . '/rows/' . $privateDocumentId, [ + $privateRow = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $privateTableId . '/rows/' . $privateRowId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]); - $this->assertEquals(401, $privateDocument['headers']['status-code']); + $this->assertEquals(401, $privateRow['headers']['status-code']); foreach ($roles as $role) { Authorization::setRole($role); } } - public function testWriteDocumentWithPermissions() + public function testWriteRowWithPermissions() { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php index 32a97aeadf..f1e5c35482 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php @@ -168,7 +168,7 @@ class DatabasesPermissionsMemberTest extends Scope $doconly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', $this->getServerHeader(), [ 'tableId' => ID::unique(), - 'name' => 'Document Only Movies', + 'name' => 'Row Only Movies', 'permissions' => [], 'rowSecurity' => true, ]); @@ -196,7 +196,7 @@ class DatabasesPermissionsMemberTest extends Scope * @dataProvider permissionsProvider * @depends testSetupDatabase */ - public function testReadDocuments($permissions, $anyCount, $usersCount, $docOnlyCount, $data) + public function testReadRows($permissions, $anyCount, $usersCount, $docOnlyCount, $data) { $users = $data['users']; $tables = $data['tables']; @@ -230,7 +230,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); /** - * Check "any" permission collection + * Check "any" permission table */ $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tables['public'] . '/rows', [ 'origin' => 'http://localhost', @@ -243,7 +243,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals($anyCount, $rows['body']['total']); /** - * Check "users" permission collection + * Check "users" permission table */ $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tables['private'] . '/rows', [ 'origin' => 'http://localhost', @@ -256,7 +256,7 @@ class DatabasesPermissionsMemberTest extends Scope $this->assertEquals($usersCount, $rows['body']['total']); /** - * Check "user:user1" document only permission collection + * Check "user:user1" row only permission table */ $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tables['doconly'] . '/rows', [ 'origin' => 'http://localhost', diff --git a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php index c62583f76c..406ca79371 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php @@ -36,7 +36,7 @@ class DatabasesPermissionsTeamTest extends Scope ]; } - public function createCollections($teams) + public function createTables($teams) { $db = $this->client->call(Client::METHOD_POST, '/databases', $this->getServerHeader(), [ 'databaseId' => $this->databaseId, @@ -45,8 +45,8 @@ class DatabasesPermissionsTeamTest extends Scope $this->assertEquals(201, $db['headers']['status-code']); $table1 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables', $this->getServerHeader(), [ - 'tableId' => ID::custom('collection1'), - 'name' => 'Collection 1', + 'tableId' => ID::custom('table1'), + 'name' => 'Table 1', 'permissions' => [ Permission::read(Role::team($teams['team1']['$id'])), Permission::create(Role::team($teams['team1']['$id'], 'admin')), @@ -55,17 +55,17 @@ class DatabasesPermissionsTeamTest extends Scope ], ]); - $this->collections['collection1'] = $table1['body']['$id']; + $this->tables['table1'] = $table1['body']['$id']; - $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection1'] . '/columns/string', $this->getServerHeader(), [ + $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->tables['table1'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, ]); $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables', $this->getServerHeader(), [ - 'tableId' => ID::custom('collection2'), - 'name' => 'Collection 2', + 'tableId' => ID::custom('table2'), + 'name' => 'Table 2', 'permissions' => [ Permission::read(Role::team($teams['team2']['$id'])), Permission::create(Role::team($teams['team2']['$id'], 'owner')), @@ -74,9 +74,9 @@ class DatabasesPermissionsTeamTest extends Scope ] ]); - $this->collections['collection2'] = $table2['body']['$id']; + $this->tables['table2'] = $table2['body']['$id']; - $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection2'] . '/columns/string', $this->getServerHeader(), [ + $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->tables['table2'] . '/columns/string', $this->getServerHeader(), [ 'key' => 'title', 'size' => 256, 'required' => true, @@ -84,22 +84,22 @@ class DatabasesPermissionsTeamTest extends Scope sleep(2); - return $this->collections; + return $this->tables; } /* * $success = can $user read from $table * [$user, $table, $success] */ - public function readDocumentsProvider(): array + public function readRowsProvider(): array { return [ - ['user1', 'collection1', true], - ['user2', 'collection1', false], - ['user3', 'collection1', true], - ['user1', 'collection2', false], - ['user2', 'collection2', true], - ['user3', 'collection2', true], + ['user1', 'table1', true], + ['user2', 'table1', false], + ['user3', 'table1', true], + ['user1', 'table2', false], + ['user2', 'table2', true], + ['user3', 'table2', true], ]; } @@ -107,15 +107,15 @@ class DatabasesPermissionsTeamTest extends Scope * $success = can $user write to $table * [$user, $table, $success] */ - public function writeDocumentsProvider(): array + public function writeRowsProvider(): array { return [ - ['user1', 'collection1', true], - ['user2', 'collection1', false], - ['user3', 'collection1', false], - ['user1', 'collection2', false], - ['user2', 'collection2', true], - ['user3', 'collection2', false], + ['user1', 'table1', true], + ['user2', 'table1', false], + ['user3', 'table1', false], + ['user1', 'table2', false], + ['user2', 'table2', true], + ['user3', 'table2', false], ]; } @@ -138,9 +138,9 @@ class DatabasesPermissionsTeamTest extends Scope $this->addToTeam('user3', 'team1'); $this->addToTeam('user3', 'team2'); - $this->createCollections($this->teams); + $this->createTables($this->teams); - $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection1'] . '/rows', $this->getServerHeader(), [ + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->tables['table1'] . '/rows', $this->getServerHeader(), [ 'rowId' => ID::unique(), 'data' => [ 'title' => 'Lorem', @@ -148,7 +148,7 @@ class DatabasesPermissionsTeamTest extends Scope ]); $this->assertEquals(201, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->collections['collection2'] . '/rows', $this->getServerHeader(), [ + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $this->tables['table2'] . '/rows', $this->getServerHeader(), [ 'rowId' => ID::unique(), 'data' => [ 'title' => 'Ipsum', @@ -162,9 +162,9 @@ class DatabasesPermissionsTeamTest extends Scope /** * Data provider params are passed before test dependencies * @depends testSetupDatabase - * @dataProvider readDocumentsProvider + * @dataProvider readRowsProvider */ - public function testReadDocuments($user, $table, $success, $users) + public function testReadRows($user, $table, $success, $users) { $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $this->databaseId . '/tables/' . $table . '/rows', [ 'origin' => 'http://localhost', @@ -182,9 +182,9 @@ class DatabasesPermissionsTeamTest extends Scope /** * @depends testSetupDatabase - * @dataProvider writeDocumentsProvider + * @dataProvider writeRowsProvider */ - public function testWriteDocuments($user, $table, $success, $users) + public function testWriteRows($user, $table, $success, $users) { $rows = $this->client->call(Client::METHOD_POST, '/databases/' . $this->databaseId . '/tables/' . $table . '/rows', [ 'origin' => 'http://localhost', From a13a6a6913d4e098d0cec8e6f353135e434a4bea Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 11:06:51 +0530 Subject: [PATCH 143/173] update: naming convention for table tests. --- .../Tables/DatabasesCustomServerTest.php | 858 +++++++++--------- 1 file changed, 429 insertions(+), 429 deletions(-) diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php index e2aebf4969..710b167b64 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php @@ -239,7 +239,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // This collection already exists + // This database already exists $response = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -325,7 +325,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); $this->assertEquals("", $response['body']); - // Try to get the collection and check if it has been deleted + // Try to get the database and check if it has been deleted $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] @@ -334,7 +334,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); } - public function testListCollections(): array + public function testListTables(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -342,10 +342,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'invalidDocumentDatabase', + 'name' => 'invalidRowDatabase', ]); $this->assertEquals(201, $database['headers']['status-code']); - $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertEquals('invalidRowDatabase', $database['body']['name']); $this->assertTrue($database['body']['enabled']); $databaseId = $database['body']['$id']; @@ -572,7 +572,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // This collection already exists + // This table already exists $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -597,9 +597,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testListCollections + * @depends testListTables */ - public function testGetCollection(array $data): void + public function testGetTable(array $data): void { $databaseId = $data['databaseId']; $tableId = $data['tableId']; @@ -617,9 +617,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testListCollections + * @depends testListTables */ - public function testUpdateCollection(array $data) + public function testUpdateTable(array $data) { $databaseId = $data['databaseId']; $tableId = $data['tableId']; @@ -640,9 +640,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testListCollections + * @depends testListTables */ - public function testCreateEncryptedAttribute(array $data): void + public function testCreateEncryptedColumn(array $data): void { $databaseId = $data['databaseId']; @@ -651,7 +651,7 @@ class DatabasesCustomServerTest extends Scope * Test for SUCCESS */ - // Create collection + // Create table $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -669,10 +669,10 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(201, $actors['headers']['status-code']); - $this->assertEquals($actors['body']['name'], 'Encrypted Actors Data'); + $this->assertEquals('Encrypted Actors Data', $actors['body']['name']); /** - * Test for creating encrypted attributes + * Test for creating encrypted columns */ $columnsPath = '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns'; @@ -700,7 +700,7 @@ class DatabasesCustomServerTest extends Scope /** - * Check status of every attribute + * Check status of every column */ $this->assertEquals(202, $firstName['headers']['status-code']); $this->assertEquals('firstName', $firstName['body']['key']); @@ -710,10 +710,10 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals('lastName', $lastName['body']['key']); $this->assertEquals('string', $lastName['body']['type']); - // Wait for database worker to finish creating attributes + // Wait for database worker to finish creating columns sleep(2); - // Creating document to ensure cache is purged on schema change + // Creating row to ensure cache is purged on schema change $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -731,7 +731,7 @@ class DatabasesCustomServerTest extends Scope ], ]); - // Check document to ensure cache is purged on schema change + // Check row to ensure cache is purged on schema change $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows/' . $row['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -743,7 +743,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals('Jameson', $row['body']['lastName']); } - public function testDeleteAttribute(): array + public function testDeleteColumn(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -751,17 +751,17 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'invalidDocumentDatabase', + 'name' => 'invalidRowDatabase', ]); $this->assertEquals(201, $database['headers']['status-code']); - $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertEquals('invalidRowDatabase', $database['body']['name']); $databaseId = $database['body']['$id']; /** * Test for SUCCESS */ - // Create collection + // Create table $actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -811,10 +811,10 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - // Wait for database worker to finish creating attributes + // Wait for database worker to finish creating columns sleep(2); - // Creating document to ensure cache is purged on schema change + // Creating row to ensure cache is purged on schema change $row = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -865,7 +865,7 @@ class DatabasesCustomServerTest extends Scope $this->assertCount(1, $table['body']['indexes']); $this->assertEquals($table['body']['indexes'][0]['key'], $index['body']['key']); - // Delete attribute + // Delete column $column = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/columns/' . $unneededId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -876,7 +876,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); - // Check document to ensure cache is purged on schema change + // Check row to ensure cache is purged on schema change $row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $actors['body']['$id'] . '/rows/' . $row['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -905,7 +905,7 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testDeleteAttribute + * @depends testDeleteColumn */ public function testDeleteIndex($data): array { @@ -935,7 +935,7 @@ class DatabasesCustomServerTest extends Scope /** * @depends testDeleteIndex */ - public function testDeleteIndexOnDeleteAttribute($data) + public function testDeleteIndexOnDeleteColumn($data) { $databaseId = $data['databaseId']; $column1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/string', array_merge([ @@ -943,7 +943,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => 'attribute1', + 'key' => 'column1', 'size' => 16, 'required' => true, ]); @@ -953,15 +953,15 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => 'attribute2', + 'key' => 'column2', 'size' => 16, 'required' => true, ]); $this->assertEquals(202, $column1['headers']['status-code']); $this->assertEquals(202, $column2['headers']['status-code']); - $this->assertEquals('attribute1', $column1['body']['key']); - $this->assertEquals('attribute2', $column2['body']['key']); + $this->assertEquals('column1', $column1['body']['key']); + $this->assertEquals('column2', $column2['body']['key']); sleep(2); @@ -972,7 +972,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index1', 'type' => 'key', - 'columns' => ['attribute1', 'attribute2'], + 'columns' => ['column1', 'column2'], 'orders' => ['ASC', 'ASC'], ]); @@ -983,7 +983,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index2', 'type' => 'key', - 'columns' => ['attribute2'], + 'columns' => ['column2'], ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -993,7 +993,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); - // Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key + // Expected behavior: deleting column2 will cause index2 to be dropped, and index1 rebuilt with a single key $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $column2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1019,7 +1019,7 @@ class DatabasesCustomServerTest extends Scope $this->assertCount(1, $table['body']['indexes'][0]['columns']); $this->assertEquals($column1['body']['key'], $table['body']['indexes'][0]['columns'][0]); - // Delete attribute + // Delete column $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $data['tableId'] . '/columns/' . $column1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1031,7 +1031,7 @@ class DatabasesCustomServerTest extends Scope return $data; } - public function testCleanupDuplicateIndexOnDeleteAttribute() + public function testCleanupDuplicateIndexOnDeleteColumn() { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -1039,10 +1039,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'invalidDocumentDatabase', + 'name' => 'invalidRowDatabase', ]); $this->assertEquals(201, $database['headers']['status-code']); - $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertEquals('invalidRowDatabase', $database['body']['name']); $databaseId = $database['body']['$id']; $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ @@ -1051,7 +1051,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'tableId' => ID::unique(), - 'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute', + 'name' => 'TestCleanupDuplicateIndexOnDeleteColumn', 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -1071,7 +1071,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => 'attribute1', + 'key' => 'column1', 'size' => 16, 'required' => true, ]); @@ -1081,15 +1081,15 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => 'attribute2', + 'key' => 'column2', 'size' => 16, 'required' => true, ]); $this->assertEquals(202, $column1['headers']['status-code']); $this->assertEquals(202, $column2['headers']['status-code']); - $this->assertEquals('attribute1', $column1['body']['key']); - $this->assertEquals('attribute2', $column2['body']['key']); + $this->assertEquals('column1', $column1['body']['key']); + $this->assertEquals('column2', $column2['body']['key']); sleep(2); @@ -1100,7 +1100,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index1', 'type' => 'key', - 'columns' => ['attribute1', 'attribute2'], + 'columns' => ['column1', 'column2'], 'orders' => ['ASC', 'ASC'], ]); @@ -1111,7 +1111,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'index2', 'type' => 'key', - 'columns' => ['attribute2'], + 'columns' => ['column2'], ]); $this->assertEquals(202, $index1['headers']['status-code']); @@ -1121,7 +1121,7 @@ class DatabasesCustomServerTest extends Scope sleep(2); - // Expected behavior: deleting attribute1 would cause index1 to be a duplicate of index2 and automatically removed + // Expected behavior: deleting column1 would cause index1 to be a duplicate of index2 and automatically removed $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $column1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1147,7 +1147,7 @@ class DatabasesCustomServerTest extends Scope $this->assertCount(1, $table['body']['indexes'][0]['columns']); $this->assertEquals($column2['body']['key'], $table['body']['indexes'][0]['columns'][0]); - // Delete attribute + // Delete column $deleted = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $column2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1158,14 +1158,14 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testDeleteIndexOnDeleteAttribute + * @depends testDeleteIndexOnDeleteColumn */ - public function testDeleteCollection($data) + public function testDeleteTable($data) { $databaseId = $data['databaseId']; $tableId = $data['tableId']; - // Add Documents to the collection + // Add Rows to the table $row1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1207,10 +1207,10 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(201, $row2['headers']['status-code']); $this->assertIsArray($row2['body']['$permissions']); $this->assertCount(3, $row2['body']['$permissions']); - $this->assertEquals($row2['body']['firstName'], 'Samuel'); - $this->assertEquals($row2['body']['lastName'], 'Jackson'); + $this->assertEquals('Samuel', $row2['body']['firstName']); + $this->assertEquals('Jackson', $row2['body']['lastName']); - // Delete the actors collection + // Delete the actors table $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1220,7 +1220,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); $this->assertEquals($response['body'], ""); - // Try to get the collection and check if it has been deleted + // Try to get the table and check if it has been deleted $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $tableId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] @@ -1232,14 +1232,14 @@ class DatabasesCustomServerTest extends Scope /** * @throws Exception */ - public function testDeleteCollectionDeletesRelatedAttributes(): void + public function testDeleteTableDeletesRelatedColumns(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'databaseId' => ID::unique(), - 'name' => 'TestDeleteCollectionDeletesRelatedAttributes', + 'name' => 'TestDeleteTableDeletesRelatedColumns', ]); $databaseId = $database['body']['$id']; @@ -1250,7 +1250,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'tableId' => ID::unique(), - 'name' => 'Collection1', + 'name' => 'Table1', 'rowSecurity' => false, 'permissions' => [], ]); @@ -1261,7 +1261,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'tableId' => ID::unique(), - 'name' => 'Collection2', + 'name' => 'Table2', 'rowSecurity' => false, 'permissions' => [], ]); @@ -1277,7 +1277,7 @@ class DatabasesCustomServerTest extends Scope 'relatedTableId' => $table2, 'type' => Database::RELATION_MANY_TO_ONE, 'twoWay' => false, - 'key' => 'collection2' + 'key' => 'table2' ]); sleep(2); @@ -1299,7 +1299,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(0, $columns['body']['total']); } - public function testAttributeRowWidthLimit() + public function testColumnRowWidthLimit() { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -1307,10 +1307,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'invalidDocumentDatabase', + 'name' => 'invalidRowDatabase', ]); $this->assertEquals(201, $database['headers']['status-code']); - $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertEquals('invalidRowDatabase', $database['body']['name']); $databaseId = $database['body']['$id']; $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ @@ -1318,8 +1318,8 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('attributeRowWidthLimit'), - 'name' => 'attributeRowWidthLimit', + 'tableId' => ID::custom('columnRowWidthLimit'), + 'name' => 'columnRowWidthLimit', 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -1330,18 +1330,18 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(201, $table['headers']['status-code']); - $this->assertEquals($table['body']['name'], 'attributeRowWidthLimit'); + $this->assertEquals('columnRowWidthLimit', $table['body']['name']); $tableId = $table['body']['$id']; - // Add wide string attributes to approach row width limit + // Add wide string columns to approach row width limit for ($i = 0; $i < 15; $i++) { $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => "attribute{$i}", + 'key' => "column{$i}", 'size' => 1024, 'required' => true, ]); @@ -1373,10 +1373,10 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'invalidDocumentDatabase', + 'name' => 'invalidRowDatabase', ]); $this->assertEquals(201, $database['headers']['status-code']); - $this->assertEquals('invalidDocumentDatabase', $database['body']['name']); + $this->assertEquals('invalidRowDatabase', $database['body']['name']); $databaseId = $database['body']['$id']; $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ @@ -1400,15 +1400,15 @@ class DatabasesCustomServerTest extends Scope $tableId = $table['body']['$id']; - // add unique attributes for indexing + // add unique columns for indexing for ($i = 0; $i < 64; $i++) { - // $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); + // $this->assertEquals(true, static::getDatabase()->createColumn('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => "attribute{$i}", + 'key' => "column{$i}", 'size' => 64, 'required' => true, ]); @@ -1425,18 +1425,18 @@ class DatabasesCustomServerTest extends Scope ])); $this->assertEquals(200, $table['headers']['status-code']); - $this->assertEquals($table['body']['name'], 'testLimitException'); + $this->assertEquals('testLimitException', $table['body']['name']); $this->assertIsArray($table['body']['columns']); $this->assertIsArray($table['body']['indexes']); $this->assertCount(64, $table['body']['columns']); $this->assertCount(0, $table['body']['indexes']); foreach ($table['body']['columns'] as $column) { - $this->assertEquals('available', $column['status'], 'attribute: ' . $column['key']); + $this->assertEquals('available', $column['status'], 'column: ' . $column['key']); } // Test indexLimit = 64 - // MariaDB, MySQL, and MongoDB create 6 indexes per new collection + // MariaDB, MySQL, and MongoDB create 6 indexes per new table // Add up to the limit, then check if the next index throws IndexLimitException for ($i = 0; $i < 58; $i++) { $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ @@ -1444,13 +1444,13 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => "key_attribute{$i}", + 'key' => "key_column{$i}", 'type' => 'key', - 'columns' => ["attribute{$i}"], + 'columns' => ["column{$i}"], ]); $this->assertEquals(202, $index['headers']['status-code']); - $this->assertEquals("key_attribute{$i}", $index['body']['key']); + $this->assertEquals("key_column{$i}", $index['body']['key']); } sleep(5); @@ -1475,7 +1475,7 @@ class DatabasesCustomServerTest extends Scope ]), [ 'key' => 'tooMany', 'type' => 'key', - 'columns' => ['attribute61'], + 'columns' => ['column61'], ]); $this->assertEquals(400, $tooMany['headers']['status-code']); @@ -1490,7 +1490,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(204, $table['headers']['status-code']); } - public function testAttributeUpdate(): array + public function testColumnUpdate(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', @@ -1498,7 +1498,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'databaseId' => ID::unique(), - 'name' => 'updateAttributes', + 'name' => 'updateColumns', ]); $this->assertEquals(201, $database['headers']['status-code']); @@ -1508,8 +1508,8 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => ID::custom('updateAttributes'), - 'name' => 'updateAttributes' + 'tableId' => ID::custom('updateColumns'), + 'name' => 'updateColumns' ]); $this->assertEquals(201, $table['headers']['status-code']); @@ -1517,7 +1517,7 @@ class DatabasesCustomServerTest extends Scope $tableId = $table['body']['$id']; /** - * Create String Attribute + * Create String Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([ 'content-type' => 'application/json', @@ -1532,7 +1532,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(202, $column['headers']['status-code']); /** - * Create Email Attribute + * Create Email Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ 'content-type' => 'application/json', @@ -1546,7 +1546,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(202, $column['headers']['status-code']); /** - * Create IP Attribute + * Create IP Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ 'content-type' => 'application/json', @@ -1560,7 +1560,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(202, $column['headers']['status-code']); /** - * Create URL Attribute + * Create URL Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ 'content-type' => 'application/json', @@ -1574,7 +1574,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(202, $column['headers']['status-code']); /** - * Create Integer Attribute + * Create Integer Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ 'content-type' => 'application/json', @@ -1588,7 +1588,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(202, $column['headers']['status-code']); /** - * Create Float Attribute + * Create Float Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([ 'content-type' => 'application/json', @@ -1600,7 +1600,7 @@ class DatabasesCustomServerTest extends Scope ]); /** - * Create Boolean Attribute + * Create Boolean Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([ 'content-type' => 'application/json', @@ -1612,7 +1612,7 @@ class DatabasesCustomServerTest extends Scope ]); /** - * Create Datetime Attribute + * Create Datetime Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([ 'content-type' => 'application/json', @@ -1624,7 +1624,7 @@ class DatabasesCustomServerTest extends Scope ]); /** - * Create Enum Attribute + * Create Enum Column */ $column = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([ 'content-type' => 'application/json', @@ -1647,9 +1647,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateString(array $data) + public function testColumnUpdateString(array $data) { $key = 'string'; $databaseId = $data['databaseId']; @@ -1789,9 +1789,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateEmail(array $data) + public function testColumnUpdateEmail(array $data) { $key = 'email'; $databaseId = $data['databaseId']; @@ -1932,9 +1932,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateIp(array $data) + public function testColumnUpdateIp(array $data) { $key = 'ip'; $databaseId = $data['databaseId']; @@ -2074,9 +2074,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateUrl(array $data) + public function testColumnUpdateUrl(array $data) { $key = 'url'; $databaseId = $data['databaseId']; @@ -2216,9 +2216,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateInteger(array $data) + public function testColumnUpdateInteger(array $data) { $key = 'integer'; $databaseId = $data['databaseId']; @@ -2477,9 +2477,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateFloat(array $data) + public function testColumnUpdateFloat(array $data) { $key = 'float'; $databaseId = $data['databaseId']; @@ -2738,9 +2738,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateBoolean(array $data) + public function testColumnUpdateBoolean(array $data) { $key = 'boolean'; $databaseId = $data['databaseId']; @@ -2880,9 +2880,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateDatetime(array $data) + public function testColumnUpdateDatetime(array $data) { $key = 'datetime'; $databaseId = $data['databaseId']; @@ -3022,9 +3022,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateEnum(array $data) + public function testColumnUpdateEnum(array $data) { $key = 'enum'; $databaseId = $data['databaseId']; @@ -3238,9 +3238,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateStringResize(array $data) + public function testColumnUpdateStringResize(array $data) { $key = 'string'; $databaseId = $data['databaseId']; @@ -3277,7 +3277,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $column['headers']['status-code']); $this->assertEquals(2048, $column['body']['size']); - // Test create new document with new size + // Test create new row with new size $newDoc = $this->client->call( Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', @@ -3298,7 +3298,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(201, $newDoc['headers']['status-code']); $this->assertEquals(2048, strlen($newDoc['body']['string'])); - // Test update document with new size + // Test update row with new size $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3326,7 +3326,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $column['headers']['status-code']); $this->assertEquals(AppwriteException::COLUMN_INVALID_RESIZE, $column['body']['type']); - // original documents to original size, remove new document + // original rows to original size, remove new row $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3363,7 +3363,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $column['headers']['status-code']); $this->assertEquals(10, $column['body']['size']); - // Test create new document with new size + // Test create new row with new size $newDoc = $this->client->call( Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', @@ -3384,7 +3384,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(201, $newDoc['headers']['status-code']); $this->assertEquals(10, strlen($newDoc['body']['string'])); - // Test update document with new size + // Test update row with new size $row = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3398,7 +3398,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $row['headers']['status-code']); $this->assertEquals(10, strlen($row['body']['string'])); - // Try create document with string that is too large + // Try create row with string that is too large $newDoc = $this->client->call( Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', @@ -3421,9 +3421,9 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeUpdateNotFound(array $data) + public function testColumnUpdateNotFound(array $data) { $databaseId = $data['databaseId']; $tableId = $data['tableId']; @@ -3482,7 +3482,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(AppwriteException::DATABASE_NOT_FOUND, $update['body']['type']); /** - * Check if Collection exists + * Check if Table exists */ $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/i_dont_exist/columns/' . $key . '/unknown_' . $key, array_merge([ 'content-type' => 'application/json', @@ -3494,7 +3494,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(AppwriteException::TABLE_NOT_FOUND, $update['body']['type']); /** - * Check if Attribute exists + * Check if Column exists */ $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $tableId . '/columns/' . $key . '/unknown_' . $key, array_merge([ 'content-type' => 'application/json', @@ -3508,15 +3508,15 @@ class DatabasesCustomServerTest extends Scope } /** - * @depends testAttributeUpdate + * @depends testColumnUpdate */ - public function testAttributeRename(array $data) + public function testColumnRename(array $data) { $key = 'string'; $databaseId = $data['databaseId']; $tableId = $data['tableId']; - // Create document to test against + // Create row to test against $row = $this->client->call( Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', @@ -3568,7 +3568,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals('string', $doc1['body']['new_string']); $this->assertArrayNotHasKey('string', $doc1['body']); - // Try and create a new document with the new attribute + // Try and create a new row with the new column $doc2 = $this->client->call( Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', @@ -3590,7 +3590,7 @@ class DatabasesCustomServerTest extends Scope $this->assertArrayHasKey('new_string', $doc2['body']); $this->assertEquals('string', $doc2['body']['new_string']); - // Expect fail, try and create a new document with the old attribute + // Expect fail, try and create a new row with the old column $doc3 = $this->client->call( Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $tableId . '/rows', @@ -3611,9 +3611,9 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $doc3['headers']['status-code']); } - public function createRelationshipCollections(): void + public function createRelationshipTables(): void { - // Prepare the database with collections and relationships + // Prepare the database with tables and relationships $database = $this->client->call(Client::METHOD_POST, '/databases', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3630,7 +3630,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => 'collection1', + 'tableId' => 'table1', 'name' => 'level1', 'rowSecurity' => false, 'permissions' => [ @@ -3646,7 +3646,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'tableId' => 'collection2', + 'tableId' => 'table2', 'name' => 'level2', 'rowSecurity' => false, 'permissions' => [ @@ -3660,7 +3660,7 @@ class DatabasesCustomServerTest extends Scope \sleep(2); } - public function cleanupRelationshipCollection(): void + public function cleanupRelationshipTable(): void { $this->client->call(Client::METHOD_DELETE, '/databases/database1', [ 'content-type' => 'application/json', @@ -3671,13 +3671,13 @@ class DatabasesCustomServerTest extends Scope \sleep(2); } - public function testAttributeRenameRelationshipOneToMany() + public function testColumnRenameRelationshipOneToMany() { $databaseId = 'database1'; - $table1Id = 'collection1'; - $table2Id = 'collection2'; + $table1Id = 'table1'; + $table2Id = 'table2'; - $this->createRelationshipCollections(); + $this->createRelationshipTables(); $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ 'content-type' => 'application/json', @@ -3694,20 +3694,20 @@ class DatabasesCustomServerTest extends Scope \sleep(3); - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + $table1RelationColumn = $table1Columns['body']['columns'][0]; - $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); - $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['side'], $table1RelationColumn['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationColumn['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationColumn['relatedTable']); - // Create a document for checking later - $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + // Create a row for checking later + $originalRow = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3722,9 +3722,9 @@ class DatabasesCustomServerTest extends Scope "permissions" => ["read(\"any\")"] ]); - $this->assertEquals(201, $originalDocument['headers']['status-code']); + $this->assertEquals(201, $originalRow['headers']['status-code']); - // Rename the attribute + // Rename the column $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3735,59 +3735,59 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $update['headers']['status-code']); - // Check the document's key has been renamed - $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + // Check the row's key has been renamed + $newRow = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalRow['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('new_level_2', $newDocument['body']); - $this->assertEquals(1, count($newDocument['body']['new_level_2'])); - $this->assertArrayNotHasKey('level2', $newDocument['body']); + $this->assertArrayHasKey('new_level_2', $newRow['body']); + $this->assertEquals(1, count($newRow['body']['new_level_2'])); + $this->assertArrayNotHasKey('level2', $newRow['body']); - // Check level2 document has been renamed - $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2'][0]['$id'], array_merge([ + // Check level2 row has been renamed + $level2Row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newRow['body']['new_level_2'][0]['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('level1', $level2Document['body']); - $this->assertNotEmpty($level2Document['body']['level1']); + $this->assertArrayHasKey('level1', $level2Row['body']); + $this->assertNotEmpty($level2Row['body']['level1']); - // Check if attribute was renamed on the parent's side - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + // Check if column was renamed on the parent's side + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table1Attributes['headers']['status-code']); - $this->assertEquals(1, count($table1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + $this->assertEquals(200, $table1Columns['headers']['status-code']); + $this->assertEquals(1, count($table1Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table1Columns['body']['columns'][0]['key']); - // Check if attribute was renamed on the child's side - $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + // Check if column was renamed on the child's side + $table2Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table2Attributes['headers']['status-code']); - $this->assertEquals(1, count($table2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(200, $table2Columns['headers']['status-code']); + $this->assertEquals(1, count($table2Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table2Columns['body']['columns'][0]['twoWayKey']); - $this->cleanupRelationshipCollection(); + $this->cleanupRelationshipTable(); } - public function testAttributeRenameRelationshipOneToOne() + public function testColumnRenameRelationshipOneToOne() { $databaseId = 'database1'; - $table1Id = 'collection1'; - $table2Id = 'collection2'; + $table1Id = 'table1'; + $table2Id = 'table2'; - $this->createRelationshipCollections(); + $this->createRelationshipTables(); $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ 'content-type' => 'application/json', @@ -3804,20 +3804,20 @@ class DatabasesCustomServerTest extends Scope \sleep(3); - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + $table1RelationColumn = $table1Columns['body']['columns'][0]; - $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); - $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['side'], $table1RelationColumn['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationColumn['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationColumn['relatedTable']); - // Create a document for checking later - $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + // Create a row for checking later + $originalRow = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3832,9 +3832,9 @@ class DatabasesCustomServerTest extends Scope "permissions" => ["read(\"any\")"] ]); - $this->assertEquals(201, $originalDocument['headers']['status-code']); + $this->assertEquals(201, $originalRow['headers']['status-code']); - // Rename the attribute + // Rename the column $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3845,59 +3845,59 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $update['headers']['status-code']); - // Check the document's key has been renamed - $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + // Check the row's key has been renamed + $newRow = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalRow['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('new_level_2', $newDocument['body']); - $this->assertNotEmpty($newDocument['body']['new_level_2']); - $this->assertArrayNotHasKey('level2', $newDocument['body']); + $this->assertArrayHasKey('new_level_2', $newRow['body']); + $this->assertNotEmpty($newRow['body']['new_level_2']); + $this->assertArrayNotHasKey('level2', $newRow['body']); - // Check level2 document has been renamed - $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2']['$id'], array_merge([ + // Check level2 row has been renamed + $level2Row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newRow['body']['new_level_2']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('level1', $level2Document['body']); - $this->assertNotEmpty($level2Document['body']['level1']); + $this->assertArrayHasKey('level1', $level2Row['body']); + $this->assertNotEmpty($level2Row['body']['level1']); - // Check if attribute was renamed on the parent's side - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + // Check if column was renamed on the parent's side + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table1Attributes['headers']['status-code']); - $this->assertEquals(1, count($table1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + $this->assertEquals(200, $table1Columns['headers']['status-code']); + $this->assertEquals(1, count($table1Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table1Columns['body']['columns'][0]['key']); - // Check if attribute was renamed on the child's side - $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + // Check if column was renamed on the child's side + $table2Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table2Attributes['headers']['status-code']); - $this->assertEquals(1, count($table2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(200, $table2Columns['headers']['status-code']); + $this->assertEquals(1, count($table2Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table2Columns['body']['columns'][0]['twoWayKey']); - $this->cleanupRelationshipCollection(); + $this->cleanupRelationshipTable(); } - public function testAttributeRenameRelationshipManyToOne() + public function testColumnRenameRelationshipManyToOne() { $databaseId = 'database1'; - $table1Id = 'collection1'; - $table2Id = 'collection2'; + $table1Id = 'table1'; + $table2Id = 'table2'; - $this->createRelationshipCollections(); + $this->createRelationshipTables(); $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ 'content-type' => 'application/json', @@ -3914,20 +3914,20 @@ class DatabasesCustomServerTest extends Scope \sleep(3); - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + $table1RelationColumn = $table1Columns['body']['columns'][0]; - $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); - $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['side'], $table1RelationColumn['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationColumn['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationColumn['relatedTable']); - // Create a document for checking later - $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + // Create a row for checking later + $originalRow = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3942,9 +3942,9 @@ class DatabasesCustomServerTest extends Scope "permissions" => ["read(\"any\")"] ]); - $this->assertEquals(201, $originalDocument['headers']['status-code']); + $this->assertEquals(201, $originalRow['headers']['status-code']); - // Rename the attribute + // Rename the column $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3955,59 +3955,59 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $update['headers']['status-code']); - // Check the document's key has been renamed - $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + // Check the row's key has been renamed + $newRow = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalRow['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('new_level_2', $newDocument['body']); - $this->assertNotEmpty($newDocument['body']['new_level_2']); - $this->assertArrayNotHasKey('level2', $newDocument['body']); + $this->assertArrayHasKey('new_level_2', $newRow['body']); + $this->assertNotEmpty($newRow['body']['new_level_2']); + $this->assertArrayNotHasKey('level2', $newRow['body']); - // Check level2 document has been renamed - $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2']['$id'], array_merge([ + // Check level2 row has been renamed + $level2Row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newRow['body']['new_level_2']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('level1', $level2Document['body']); - $this->assertNotEmpty($level2Document['body']['level1']); + $this->assertArrayHasKey('level1', $level2Row['body']); + $this->assertNotEmpty($level2Row['body']['level1']); - // Check if attribute was renamed on the parent's side - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + // Check if column was renamed on the parent's side + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table1Attributes['headers']['status-code']); - $this->assertEquals(1, count($table1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + $this->assertEquals(200, $table1Columns['headers']['status-code']); + $this->assertEquals(1, count($table1Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table1Columns['body']['columns'][0]['key']); - // Check if attribute was renamed on the child's side - $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + // Check if column was renamed on the child's side + $table2Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table2Attributes['headers']['status-code']); - $this->assertEquals(1, count($table2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(200, $table2Columns['headers']['status-code']); + $this->assertEquals(1, count($table2Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table2Columns['body']['columns'][0]['twoWayKey']); - $this->cleanupRelationshipCollection(); + $this->cleanupRelationshipTable(); } - public function testAttributeRenameRelationshipManyToMany() + public function testColumnRenameRelationshipManyToMany() { $databaseId = 'database1'; - $table1Id = 'collection1'; - $table2Id = 'collection2'; + $table1Id = 'table1'; + $table2Id = 'table2'; - $this->createRelationshipCollections(); + $this->createRelationshipTables(); $relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/relationship', array_merge([ 'content-type' => 'application/json', @@ -4024,20 +4024,20 @@ class DatabasesCustomServerTest extends Scope \sleep(3); - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $table1RelationAttribute = $table1Attributes['body']['columns'][0]; + $table1RelationColumn = $table1Columns['body']['columns'][0]; - $this->assertEquals($relation['body']['side'], $table1RelationAttribute['side']); - $this->assertEquals($relation['body']['twoWayKey'], $table1RelationAttribute['twoWayKey']); - $this->assertEquals($relation['body']['relatedTable'], $table1RelationAttribute['relatedTable']); + $this->assertEquals($relation['body']['side'], $table1RelationColumn['side']); + $this->assertEquals($relation['body']['twoWayKey'], $table1RelationColumn['twoWayKey']); + $this->assertEquals($relation['body']['relatedTable'], $table1RelationColumn['relatedTable']); - // Create a document for checking later - $originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ + // Create a row for checking later + $originalRow = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4052,9 +4052,9 @@ class DatabasesCustomServerTest extends Scope "permissions" => ["read(\"any\")"] ]); - $this->assertEquals(201, $originalDocument['headers']['status-code']); + $this->assertEquals(201, $originalRow['headers']['status-code']); - // Rename the attribute + // Rename the column $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/tables/' . $table1Id . '/columns/level2' . '/relationship', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4065,50 +4065,50 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $update['headers']['status-code']); - // Check the document's key has been renamed - $newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalDocument['body']['$id'], array_merge([ + // Check the row's key has been renamed + $newRow = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id . '/rows/' . $originalRow['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('new_level_2', $newDocument['body']); - $this->assertNotEmpty($newDocument['body']['new_level_2']); - $this->assertArrayNotHasKey('level2', $newDocument['body']); + $this->assertArrayHasKey('new_level_2', $newRow['body']); + $this->assertNotEmpty($newRow['body']['new_level_2']); + $this->assertArrayNotHasKey('level2', $newRow['body']); - // Check level2 document has been renamed - $level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newDocument['body']['new_level_2']['$id'], array_merge([ + // Check level2 row has been renamed + $level2Row = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id . '/rows/' . $newRow['body']['new_level_2']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - $this->assertArrayHasKey('level1', $level2Document['body']); - $this->assertNotEmpty($level2Document['body']['level1']); + $this->assertArrayHasKey('level1', $level2Row['body']); + $this->assertNotEmpty($level2Row['body']['level1']); - // Check if attribute was renamed on the parent's side - $table1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ + // Check if column was renamed on the parent's side + $table1Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table1Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table1Attributes['headers']['status-code']); - $this->assertEquals(1, count($table1Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table1Attributes['body']['columns'][0]['key']); + $this->assertEquals(200, $table1Columns['headers']['status-code']); + $this->assertEquals(1, count($table1Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table1Columns['body']['columns'][0]['key']); - // Check if attribute was renamed on the child's side - $table2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ + // Check if column was renamed on the child's side + $table2Columns = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $table2Id, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->assertEquals(200, $table2Attributes['headers']['status-code']); - $this->assertEquals(1, count($table2Attributes['body']['columns'])); - $this->assertEquals('new_level_2', $table2Attributes['body']['columns'][0]['twoWayKey']); + $this->assertEquals(200, $table2Columns['headers']['status-code']); + $this->assertEquals(1, count($table2Columns['body']['columns'])); + $this->assertEquals('new_level_2', $table2Columns['body']['columns'][0]['twoWayKey']); - $this->cleanupRelationshipCollection(); + $this->cleanupRelationshipTable(); } public function testBulkCreate(): void @@ -4127,7 +4127,7 @@ class DatabasesCustomServerTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4143,15 +4143,15 @@ class DatabasesCustomServerTest extends Scope ], ]); - $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertEquals(201, $table['headers']['status-code']); $data = [ - '$id' => $collection['body']['$id'], - 'databaseId' => $collection['body']['databaseId'] + '$id' => $table['body']['$id'], + 'databaseId' => $table['body']['databaseId'] ]; - // Await attribute - $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + // Await column + $numberColumn = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4160,7 +4160,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $numberAttribute['headers']['status-code']); + $this->assertEquals(202, $numberColumn['headers']['status-code']); sleep(1); @@ -4199,7 +4199,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(3, $response['body']['rows']); - // TEST SUCCESS - $id is auto-assigned if not included in bulk documents + // TEST SUCCESS - $id is auto-assigned if not included in bulk rows $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4213,7 +4213,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); - // TEST FAIL - Can't use data and document together + // TEST FAIL - Can't use data and row together $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4231,7 +4231,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // TEST FAIL - Can't use $rowId and create bulk documents + // TEST FAIL - Can't use $rowId and create bulk rows $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4247,7 +4247,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // TEST FAIL - Can't include invalid ID in bulk documents + // TEST FAIL - Can't include invalid ID in bulk rows $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4262,7 +4262,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // TEST FAIL - Can't miss number in bulk documents + // TEST FAIL - Can't miss number in bulk rows $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4280,7 +4280,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // TEST FAIL - Can't push more than APP_LIMIT_DATABASE_BATCH documents + // TEST FAIL - Can't push more than APP_LIMIT_DATABASE_BATCH rows $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4293,7 +4293,7 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // TEST FAIL - Can't include invalid permissions in nested documents + // TEST FAIL - Can't include invalid permissions in nested rows $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/tables/{$data['$id']}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4307,8 +4307,8 @@ class DatabasesCustomServerTest extends Scope ], ]); - // TEST FAIL - Can't bulk create in a collection with relationships - $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + // TEST FAIL - Can't bulk create in a table with relationships + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4324,7 +4324,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], $this->getHeaders()), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedTableId' => $table2['body']['$id'], 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -4365,7 +4365,7 @@ class DatabasesCustomServerTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4381,15 +4381,15 @@ class DatabasesCustomServerTest extends Scope ], ]); - $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertEquals(201, $table['headers']['status-code']); $data = [ - '$id' => $collection['body']['$id'], - 'databaseId' => $collection['body']['databaseId'] + '$id' => $table['body']['$id'], + 'databaseId' => $table['body']['databaseId'] ]; - // Await attribute - $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + // Await column + $numberColumn = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4398,17 +4398,17 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $numberAttribute['headers']['status-code']); + $this->assertEquals(202, $numberColumn['headers']['status-code']); - // Wait for database worker to create attributes + // Wait for database worker to create columns sleep(2); - // Create documents - $createBulkDocuments = function ($amount = 10) use ($data) { - $documents = []; + // Create rows + $createBulkRows = function ($amount = 10) use ($data) { + $rows = []; for ($x = 1; $x <= $amount; $x++) { - $documents[] = [ + $rows[] = [ '$id' => ID::unique(), 'number' => $x, ]; @@ -4418,15 +4418,15 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rows' => $documents, + 'rows' => $rows, ]); $this->assertEquals(201, $doc['headers']['status-code']); }; - $createBulkDocuments(); + $createBulkRows(); - // TEST: Update all documents + // TEST: Update all rows $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4444,30 +4444,30 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(10, $response['body']['rows']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ Query::equal('number', [100])->toString(), ]); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(10, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(10, $rows['body']['total']); - $returnedDocuments = $response['body']['rows']; - $refetchedDocuments = $documents['body']['rows']; + $returnedRows = $response['body']['rows']; + $refetchedRows = $rows['body']['rows']; - $this->assertEquals($returnedDocuments, $refetchedDocuments); + $this->assertEquals($returnedRows, $refetchedRows); - foreach ($documents['body']['rows'] as $document) { + foreach ($rows['body']['rows'] as $row) { $this->assertEquals([ Permission::read(Role::user($this->getUser()['$id'])), Permission::update(Role::user($this->getUser()['$id'])), Permission::delete(Role::user($this->getUser()['$id'])), - ], $document['$permissions']); - $this->assertEquals($collection['body']['$id'], $document['$tableId']); - $this->assertEquals($data['databaseId'], $document['$databaseId']); - $this->assertEquals(100, $document['number']); + ], $row['$permissions']); + $this->assertEquals($table['body']['$id'], $row['$tableId']); + $this->assertEquals($data['databaseId'], $row['$databaseId']); + $this->assertEquals(100, $row['number']); } // TEST: Check permissions persist @@ -4483,25 +4483,25 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(10, $response['body']['rows']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ Query::equal('number', [200])->toString(), ]); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(10, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(10, $rows['body']['total']); - foreach ($documents['body']['rows'] as $document) { + foreach ($rows['body']['rows'] as $row) { $this->assertEquals([ Permission::read(Role::user($this->getUser()['$id'])), Permission::update(Role::user($this->getUser()['$id'])), Permission::delete(Role::user($this->getUser()['$id'])), - ], $document['$permissions']); + ], $row['$permissions']); } - // TEST: Update documents with limit + // TEST: Update rows with limit $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4517,17 +4517,17 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(5, $response['body']['rows']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [Query::equal('number', [200])->toString()] ]); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(5, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(5, $rows['body']['total']); - // TEST: Update documents with offset + // TEST: Update rows with offset $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4543,17 +4543,17 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(5, $response['body']['rows']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [Query::equal('number', [300])->toString()] ]); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(10, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(10, $rows['body']['total']); - // TEST: Update documents with equals filter + // TEST: Update rows with equals filter $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4569,18 +4569,18 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(10, $response['body']['rows']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [Query::equal('number', [400])->toString()] ]); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(10, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(10, $rows['body']['total']); - // TEST: Fail - Can't bulk update in a collection with relationships - $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + // TEST: Fail - Can't bulk update in a table with relationships + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4596,7 +4596,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], $this->getHeaders()), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedTableId' => $table2['body']['$id'], 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -4639,7 +4639,7 @@ class DatabasesCustomServerTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4655,15 +4655,15 @@ class DatabasesCustomServerTest extends Scope ], ]); - $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertEquals(201, $table['headers']['status-code']); $data = [ - '$id' => $collection['body']['$id'], - 'databaseId' => $collection['body']['databaseId'] + '$id' => $table['body']['$id'], + 'databaseId' => $table['body']['databaseId'] ]; - // Await attribute - $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + // Await column + $numberColumn = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4672,17 +4672,17 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $numberAttribute['headers']['status-code']); + $this->assertEquals(202, $numberColumn['headers']['status-code']); - // Wait for database worker to create attributes + // Wait for database worker to create columns sleep(2); - // Create documents - $createBulkDocuments = function ($amount = 10) use ($data) { - $documents = []; + // Create rows + $createBulkRows = function ($amount = 10) use ($data) { + $rows = []; for ($x = 1; $x <= $amount; $x++) { - $documents[] = [ + $rows[] = [ '$id' => "$x", 'number' => $x, ]; @@ -4692,53 +4692,53 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rows' => $documents, + 'rows' => $rows, ]); $this->assertEquals(201, $response['headers']['status-code']); - return $documents; + return $rows; }; - $documents = $createBulkDocuments(); + $rows = $createBulkRows(); - // Update 1 document - $documents[\array_key_last($documents)]['number'] = 1000; + // Update 1 row + $rows[\array_key_last($rows)]['number'] = 1000; - // Add 1 document - $documents[] = ['number' => 11]; + // Add 1 row + $rows[] = ['number' => 11]; - // TEST: Upsert all documents + // TEST: Upsert all rows $response = $this->client->call(Client::METHOD_PUT, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rows' => $documents, + 'rows' => $rows, ]); - // Unchanged docs are skipped. 2 documents should be returned, 1 updated and 1 inserted. + // Unchanged docs are skipped. 2 rows should be returned, 1 updated and 1 inserted. $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(2, $response['body']['rows']); $this->assertEquals(1000, $response['body']['rows'][0]['number']); $this->assertEquals(11, $response['body']['rows'][1]['number']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ])); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(11, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(11, $rows['body']['total']); - foreach ($documents['body']['rows'] as $index => $document) { - $this->assertEquals($collection['body']['$id'], $document['$tableId']); - $this->assertEquals($data['databaseId'], $document['$databaseId']); + foreach ($rows['body']['rows'] as $index => $row) { + $this->assertEquals($table['body']['$id'], $row['$tableId']); + $this->assertEquals($data['databaseId'], $row['$databaseId']); switch ($index) { case 9: - $this->assertEquals(1000, $document['number']); + $this->assertEquals(1000, $row['number']); break; default: - $this->assertEquals($index + 1, $document['number']); + $this->assertEquals($index + 1, $row['number']); } } @@ -4772,8 +4772,8 @@ class DatabasesCustomServerTest extends Scope Permission::delete(Role::user($this->getUser()['$id'])), ], $response['body']['rows'][1]['$permissions']); - // TEST: Fail - Can't bulk upsert in a collection with relationships - $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + // TEST: Fail - Can't bulk upsert in a table with relationships + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4789,7 +4789,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], $this->getHeaders()), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedTableId' => $table2['body']['$id'], 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', @@ -4832,7 +4832,7 @@ class DatabasesCustomServerTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + $table = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4847,15 +4847,15 @@ class DatabasesCustomServerTest extends Scope ], ]); - $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertEquals(201, $table['headers']['status-code']); $data = [ - '$id' => $collection['body']['$id'], - 'databaseId' => $collection['body']['databaseId'] + '$id' => $table['body']['$id'], + 'databaseId' => $table['body']['databaseId'] ]; - // Await attribute - $numberAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ + // Await column + $numberColumn = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables/' . $data['$id'] . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4864,17 +4864,17 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $numberAttribute['headers']['status-code']); + $this->assertEquals(202, $numberColumn['headers']['status-code']); - // wait for database worker to create attributes + // wait for database worker to create columns sleep(2); - // Create documents - $createBulkDocuments = function ($amount = 11) use ($data) { - $documents = []; + // Create rows + $createBulkRows = function ($amount = 11) use ($data) { + $rows = []; for ($x = 0; $x < $amount; $x++) { - $documents[] = [ + $rows[] = [ '$id' => ID::unique(), 'number' => $x, ]; @@ -4884,23 +4884,23 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'rows' => $documents, + 'rows' => $rows, ]); $this->assertEquals(201, $doc['headers']['status-code']); }; - $createBulkDocuments(); + $createBulkRows(); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(11, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(11, $rows['body']['total']); - // TEST: Delete all documents + // TEST: Delete all rows $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4909,24 +4909,24 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(11, $response['body']['total']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(0, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(0, $rows['body']['total']); - // TEST: Delete documents with query - $createBulkDocuments(); + // TEST: Delete rows with query + $createBulkRows(); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(11, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(11, $rows['body']['total']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -4940,16 +4940,16 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(5, $response['body']['total']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(6, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(6, $rows['body']['total']); - foreach ($documents['body']['rows'] as $document) { - $this->assertGreaterThanOrEqual(5, $document['number']); + foreach ($rows['body']['rows'] as $row) { + $this->assertGreaterThanOrEqual(5, $row['number']); } // Cleanup @@ -4961,16 +4961,16 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(6, $response['body']['total']); - // SUCCESS: Delete documents with query - $createBulkDocuments(); + // SUCCESS: Delete rows with query + $createBulkRows(); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(11, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(11, $rows['body']['total']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -4984,13 +4984,13 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(5, $response['body']['total']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(6, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(6, $rows['body']['total']); // Cleanup $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ @@ -5001,16 +5001,16 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(6, $response['body']['total']); - // SUCCESS: Delete Documents with limit query - $createBulkDocuments(); + // SUCCESS: Delete Rows with limit query + $createBulkRows(); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(11, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(11, $rows['body']['total']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -5024,13 +5024,13 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(2, $response['body']['total']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(9, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(9, $rows['body']['total']); // Cleanup $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ @@ -5041,16 +5041,16 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(9, $response['body']['total']); - // SUCCESS: Delete Documents with offset query - $createBulkDocuments(); + // SUCCESS: Delete Rows with offset query + $createBulkRows(); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(11, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(11, $rows['body']['total']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -5064,15 +5064,15 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(6, $response['body']['total']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(5, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(5, $rows['body']['total']); - $lastDoc = end($documents['body']['rows']); + $lastDoc = end($rows['body']['rows']); $this->assertNotEmpty($lastDoc); $this->assertEquals(4, $lastDoc['number']); @@ -5086,16 +5086,16 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(5, $response['body']['total']); - // SUCCESS: Delete 100 documents - $createBulkDocuments(100); + // SUCCESS: Delete 100 rows + $createBulkRows(100); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(100, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(100, $rows['body']['total']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -5105,16 +5105,16 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(100, $response['body']['total']); - $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(0, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(0, $rows['body']['total']); - // TEST: Fail - Can't bulk delete in a collection with relationships - $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ + // TEST: Fail - Can't bulk delete in a table with relationships + $table2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/tables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -5130,7 +5130,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ], $this->getHeaders()), [ - 'relatedTableId' => $collection2['body']['$id'], + 'relatedTableId' => $table2['body']['$id'], 'type' => 'manyToOne', 'twoWay' => true, 'onDelete' => 'cascade', From 2e05844ca2242feca29d0afece230b483b020a64 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 14:04:23 +0530 Subject: [PATCH 144/173] add: graphql tests for bulk apis. --- .../Collections/Documents/Bulk/Upsert.php | 1 - .../Collections/Documents/Create.php | 13 +- .../Databases/Tables/Rows/Bulk/Upsert.php | 1 - tests/e2e/Services/GraphQL/Base.php | 135 ++++++++- .../Collections/DatabaseClientTest.php | 176 +++++++++++ .../Collections/DatabaseServerTest.php | 1 + .../GraphQL/Tables/DatabaseClientTest.php | 283 ++++++++++++++++++ .../GraphQL/Tables/DatabaseServerTest.php | 1 + 8 files changed, 601 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 1e7d838fba..770f7a4d95 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -43,7 +43,6 @@ class Upsert extends Action ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') ->desc('Create or update documents') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'document.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 33406044f1..e68a27ba34 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -418,6 +418,13 @@ class Create extends Action $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); + // BIG TODO: @itznotabug - need to check what to do for bulk api because there's not just one `[document/rowId]`. + $queueForEvents + ->setParam('documentId', $documents[0]->getId()) + ->setParam('rowId', $documents[0]->getId()) + // TODO: @itznotabug - check if the events mirroring works here! + ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); + if ($isBulk) { $response->dynamic(new Document([ 'total' => count($documents), @@ -427,12 +434,6 @@ class Create extends Action return; } - $queueForEvents - ->setParam('documentId', $documents[0]->getId()) - ->setParam('rowId', $documents[0]->getId()) - // TODO: @itznotabug - check if the events mirroring works here! - ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); - $response->dynamic( $documents[0], $this->getResponseModel() diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php index 9331e75aa8..2b4117c23e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php @@ -38,7 +38,6 @@ class Upsert extends DocumentsUpsert ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Create or update rows') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.create') diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index f050fa9638..f001265caf 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -99,15 +99,29 @@ trait Base public static string $GET_DOCUMENTS = 'list_documents'; public static string $GET_DOCUMENT = 'get_document'; public static string $UPDATE_DOCUMENT = 'update_document'; + public static string $UPSERT_DOCUMENT = 'upsert_document'; public static string $DELETE_DOCUMENT = 'delete_document'; + // Documents Bulk APIs + public static string $CREATE_DOCUMENTS = 'create_documents_rest'; + public static string $UPDATE_DOCUMENTS = 'update_documents'; + public static string $UPSERT_DOCUMENTS = 'upsert_documents'; + public static string $DELETE_DOCUMENTS = 'delete_documents'; + // Rows public static string $CREATE_ROW = 'create_row_rest'; public static string $GET_ROWS = 'list_rows'; public static string $GET_ROW = 'get_row'; public static string $UPDATE_ROW = 'update_row'; + public static string $UPSERT_ROW = 'upsert_row'; public static string $DELETE_ROW = 'delete_row'; + // Rows Bulk APIs + public static string $CREATE_ROWS = 'create_rows_rest'; + public static string $UPDATE_ROWS = 'update_rows'; + public static string $UPSERT_ROWS = 'upsert_rows'; + public static string $DELETE_ROWS = 'delete_rows'; + // Custom Entities public static string $CREATE_CUSTOM_ENTITY = 'create_custom_entity'; public static string $GET_CUSTOM_ENTITIES = 'get_custom_entities'; @@ -1100,6 +1114,28 @@ trait Base _permissions } }'; + case self::$CREATE_DOCUMENTS: + return 'mutation createDocuments($databaseId: String!, $collectionId: String!, $documents: [Json!]!) { + collectionsCreateDocuments(databaseId: $databaseId, collectionId: $collectionId, documents: $documents) { + documents { + _id + _collectionId + _permissions + data + } + } + }'; + case self::$CREATE_ROWS: + return 'mutation createRows($databaseId: String!, $tableId: String!, $rows: [Json!]!) { + tablesCreateRows(databaseId: $databaseId, tableId: $tableId, rows: $rows) { + rows { + _id + _tableId + _permissions + data + } + } + }'; case self::$GET_ROW: return 'query getRow($databaseId: String!, $tableId: String!, $rowId: String!) { tablesGetRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { @@ -1110,11 +1146,12 @@ trait Base } }'; case self::$GET_ROWS: - return 'query listRows($databaseId: String!, $tableId: String!) { - tablesListRows(databaseId: $databaseId, tableId: $tableId) { + return 'query listRows($databaseId: String!, $tableId: String!, $queries: [String!] = []) { + tablesListRows(databaseId: $databaseId, tableId: $tableId, queries: $queries) { total rows { _id + _databaseId _tableId _permissions data @@ -1196,6 +1233,53 @@ trait Base data } }'; + case self::$UPSERT_DOCUMENT: + return 'mutation upsertDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!] = []) { + collectionsUpsertDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + _id + _databaseId + _collectionId + data + } + }'; + case self::$UPDATE_DOCUMENTS: + return 'mutation updateDocuments($databaseId: String!, $collectionId: String!, $data: Json!, $queries: [String!]) { + collectionsUpdateDocuments(databaseId: $databaseId, collectionId: $collectionId, data: $data, queries: $queries) { + total + documents { + _id + _databaseId + _collectionId + _permissions + data + } + } + }'; + case self::$UPSERT_DOCUMENTS: + return 'mutation upsertDocuments($databaseId: String!, $collectionId: String!, $documents: [Json!]!) { + collectionsUpsertDocuments(databaseId: $databaseId, collectionId: $collectionId, documents: $documents) { + total + documents { + _id + _databaseId + _collectionId + _permissions + data + } + } + }'; + case self::$DELETE_DOCUMENTS: + return 'mutation deleteDocuments($databaseId: String!, $collectionId: String!, $queries: [String!] = []) { + collectionsDeleteDocuments(databaseId: $databaseId, collectionId: $collectionId, queries: $queries) { + total + documents { + _id + _databaseId + _collectionId + data + } + } + }'; case self::$DELETE_DOCUMENT: return 'mutation deleteDocument($databaseId: String!, $collectionId: String!, $documentId: String!){ collectionsDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { @@ -1210,12 +1294,59 @@ trait Base data } }'; + case self::$UPSERT_ROW: + return 'mutation upsertRow($databaseId: String!, $tableId: String!, $rowId: String!, $data: Json!, $permissions: [String!] = []) { + tablesUpsertRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId, data: $data, permissions: $permissions) { + _id + _databaseId + _tableId + data + } + }'; case self::$DELETE_ROW: return 'mutation deleteRow($databaseId: String!, $tableId: String!, $rowId: String!) { tablesDeleteRow(databaseId: $databaseId, tableId: $tableId, rowId: $rowId) { status } }'; + case self::$UPDATE_ROWS: + return 'mutation updateRows($databaseId: String!, $tableId: String!, $data: Json!, $queries: [String!]) { + tablesUpdateRows(databaseId: $databaseId, tableId: $tableId, data: $data, queries: $queries) { + total + rows { + _id + _databaseId + _tableId + _permissions + data + } + } + }'; + case self::$UPSERT_ROWS: + return 'mutation upsertRows($databaseId: String!, $tableId: String!, $rows: [Json!]!) { + tablesUpsertRows(databaseId: $databaseId, tableId: $tableId, rows: $rows) { + total + rows { + _id + _databaseId + _tableId + _permissions + data + } + } + }'; + case self::$DELETE_ROWS: + return 'mutation deleteRows($databaseId: String!, $tableId: String!, $queries: [String!] = []) { + tablesDeleteRows(databaseId: $databaseId, tableId: $tableId, queries: $queries) { + total + rows { + _id + _databaseId + _tableId + data + } + } + }'; case self::$GET_USER: return 'query getUser($userId : String!) { usersGet(userId : $userId) { diff --git a/tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php index 9c23a8d784..349d580d64 100644 --- a/tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php @@ -303,4 +303,180 @@ class DatabaseClientTest extends Scope $this->assertIsNotArray($document['body']); $this->assertEquals(204, $document['headers']['status-code']); } + + /** + * @throws \Exception + */ + public function testBulkCreateDocuments(): array + { + $project = $this->getProject(); + $projectId = $project['$id']; + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $project['apiKey'], + ]; + + // Step 1: Create database + $query = $this->getQuery(self::$CREATE_DATABASE); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => 'bulk', + 'name' => 'Bulk', + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $databaseId = $res['body']['data']['databasesCreate']['_id']; + + // Step 2: Create collection + $query = $this->getQuery(self::$CREATE_COLLECTION); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'collectionId' => 'operations', + 'name' => 'Operations', + 'documentSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $collectionId = $res['body']['data']['databasesCreateCollection']['_id']; + + // Step 3: Create attribute + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + sleep(1); + + // Step 4: Create documents + $query = $this->getQuery(self::$CREATE_DOCUMENTS); + $documents = []; + for ($i = 1; $i <= 10; $i++) { + $documents[] = ['$id' => 'doc' . $i, 'name' => 'Doc #' . $i]; + } + + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documents' => $documents, + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['collectionsCreateDocuments']['documents']); + + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'projectId' => $projectId, + ]; + } + + /** + * @depends testBulkCreateDocuments + */ + public function testBulkUpdateDocuments(array $data): array + { + $userId = $this->getUser()['$id']; + $permissions = [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ]; + + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]; + + $query = $this->getQuery(self::$UPDATE_DOCUMENTS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'collectionId' => $data['collectionId'], + 'data' => [ + 'name' => 'Docs Updated', + '$permissions' => $permissions, + ], + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['collectionsUpdateDocuments']['documents']); + + return $data; + } + + /** + * @depends testBulkUpdateDocuments + */ + public function testBulkUpsertDocuments(array $data): array + { + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]; + + // Upsert: Update one, insert one + $query = $this->getQuery(self::$UPSERT_DOCUMENTS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'collectionId' => $data['collectionId'], + 'documents' => [ + ['$id' => 'doc10', 'name' => 'Doc #1000'], + ['name' => 'Doc #11'], + ], + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(2, $res['body']['data']['collectionsUpsertDocuments']['documents']); + + return $data; + } + + /** + * @depends testBulkUpsertDocuments + */ + public function testBulkDeleteDocuments(array $data): array + { + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]; + + $query = $this->getQuery(self::$DELETE_DOCUMENTS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'collectionId' => $data['collectionId'], + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(11, $res['body']['data']['collectionsDeleteDocuments']['documents']); + + return $data; + } } diff --git a/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php index 4aa540a47e..f60acb9b0b 100644 --- a/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php @@ -134,6 +134,7 @@ class DatabaseServerTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); + // TODO: @itznotabug - check for `encrypt` attribute in string column's response body as well! $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); $this->assertIsArray($attribute['body']['data']['collectionsCreateStringAttribute']); diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php index 3296f2d622..c5cba6c47f 100644 --- a/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php @@ -10,6 +10,7 @@ use Tests\E2E\Services\GraphQL\Base; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Query; class DatabaseClientTest extends Scope { @@ -303,4 +304,286 @@ class DatabaseClientTest extends Scope $this->assertIsNotArray($row['body']); $this->assertEquals(204, $row['headers']['status-code']); } + + /** + * @throws \Exception + */ + public function testBulkCreate(): array + { + $project = $this->getProject(); + $projectId = $project['$id']; + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $project['apiKey'], + ]; + + // Step 1: Create database + $query = $this->getQuery(self::$CREATE_DATABASE); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => 'bulk', + 'name' => 'Bulk', + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $databaseId = $res['body']['data']['databasesCreate']['_id']; + + // Step 2: Create table + $query = $this->getQuery(self::$CREATE_TABLE); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'tableId' => 'operations', + 'name' => 'Operations', + 'rowSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $tableId = $res['body']['data']['databasesCreateTable']['_id']; + + // Step 3: Create column + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + sleep(1); + + // Step 4: Create rows + $query = $this->getQuery(self::$CREATE_ROWS); + $rows = []; + for ($i = 1; $i <= 10; $i++) { + $rows[] = ['$id' => 'row' . $i, 'name' => 'Row #' . $i]; + } + + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rows' => $rows, + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['tablesCreateRows']['rows']); + + return compact('databaseId', 'tableId', 'projectId'); + } + + /** + * @depends testBulkCreate + */ + public function testBulkUpdate(array $data): array + { + $userId = $this->getUser()['$id']; + $permissions = [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ]; + + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]; + + // Step 1: Bulk update rows + $query = $this->getQuery(self::$UPDATE_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'data' => [ + 'name' => 'Rows Updated', + '$permissions' => $permissions, + ], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['tablesUpdateRows']['rows']); + + // Step 2: Fetch and validate updated rows + $query = $this->getQuery(self::$GET_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'queries' => [Query::equal('name', ['Rows Updated'])->toString()], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertEquals(200, $res['headers']['status-code']); + + $fetched = $res['body']['data']['tablesListRows']; + $this->assertEquals(10, $fetched['total']); + + foreach ($fetched['rows'] as $row) { + $this->assertEquals($permissions, $row['_permissions']); + $this->assertEquals($data['tableId'], $row['_tableId']); + $this->assertEquals($data['databaseId'], $row['_databaseId']); + $this->assertEquals('Rows Updated', json_decode($row['data'], true)['name']); + } + + return $data; + } + + /** + * @depends testBulkCreate + */ + public function testBulkUpsert(array $data): array + { + $userId = $this->getUser()['$id']; + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]; + + $permissions = [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ]; + + // Step 1: Mutate row 10 and add row 11 + $query = $this->getQuery(self::$UPSERT_ROWS); + $upsertPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'rows' => [ + [ + '$id' => 'row10', + 'name' => 'Row #1000', + ], + [ + 'name' => 'Row #11', + ], + ], + ], + ]; + + $response = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $upsertPayload); + $this->assertArrayNotHasKey('errors', $response['body']); + + $rows = $response['body']['data']['tablesUpsertRows']['rows']; + $this->assertCount(2, $rows); + + $rowMap = []; + foreach ($rows as $row) { + $decoded = json_decode($row['data'], true); + $rowMap[$decoded['name']] = $decoded; + } + + $this->assertArrayHasKey('Row #1000', $rowMap); + $this->assertArrayHasKey('Row #11', $rowMap); + + // Step 2: Fetch all rows and confirm count is now 11 + $query = $this->getQuery(self::$GET_ROWS); + $fetchPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $fetchPayload); + $this->assertEquals(200, $res['headers']['status-code']); + + $fetched = $res['body']['data']['tablesListRows']; + $this->assertEquals(11, $fetched['total']); + + // Step 3: Upsert row with new permissions using `tablesUpsertRow` + $query = $this->getQuery(self::$UPSERT_ROW); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'rowId' => 'row10', + 'data' => ['name' => 'Row #10 Patched'], + 'permissions' => $permissions, + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + + $updated = $res['body']['data']['tablesUpsertRow']; + $this->assertEquals('Row #10 Patched', json_decode($updated['data'], true)['name']); + $this->assertEquals($data['databaseId'], $updated['_databaseId']); + $this->assertEquals($data['tableId'], $updated['_tableId']); + + return $data; + } + + /** + * @depends testBulkUpsert + */ + public function testBulkDelete(array $data): array + { + $headers = [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]; + + // Step 1: Perform bulk delete + $query = $this->getQuery(self::$DELETE_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + + $deleted = $res['body']['data']['tablesDeleteRows']['rows']; + $this->assertIsArray($deleted); + $this->assertCount(11, $deleted); + + // Step 2: Confirm deletion via refetch + $query = $this->getQuery(self::$GET_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertEquals(200, $res['headers']['status-code']); + $this->assertEquals(0, $res['body']['data']['tablesListRows']['total']); + + return $data; + } } diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php index 4f711148e3..af72bb8a9f 100644 --- a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php @@ -134,6 +134,7 @@ class DatabaseServerTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); + // TODO: @itznotabug - check for `encrypt` attribute in string column's response body as well! $this->assertArrayNotHasKey('errors', $column['body']); $this->assertIsArray($column['body']['data']); $this->assertIsArray($column['body']['data']['tablesCreateStringColumn']); From 6e1fa0526d627e0e5f97dcf2a5c9b18c6f2dfb0f Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 14:07:33 +0530 Subject: [PATCH 145/173] add: graphql tests for bulk apis to server tests. --- .../Collections/DatabaseServerTest.php | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php index f60acb9b0b..3e5b457308 100644 --- a/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php @@ -1465,4 +1465,176 @@ class DatabaseServerTest extends Scope $this->assertIsNotArray($database['body']); $this->assertEquals(204, $database['headers']['status-code']); } + + /** + * @throws Exception + */ + public function testBulkCreateDocuments(): array + { + $project = $this->getProject(); + $projectId = $project['$id']; + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()); + + // Step 1: Create database + $query = $this->getQuery(self::$CREATE_DATABASE); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => 'bulk', + 'name' => 'Bulk', + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $databaseId = $res['body']['data']['databasesCreate']['_id']; + + // Step 2: Create collection + $query = $this->getQuery(self::$CREATE_COLLECTION); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'collectionId' => 'operations', + 'name' => 'Operations', + 'documentSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $collectionId = $res['body']['data']['databasesCreateCollection']['_id']; + + // Step 3: Create attribute + $query = $this->getQuery(self::$CREATE_STRING_ATTRIBUTE); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + sleep(1); + + // Step 4: Create documents + $query = $this->getQuery(self::$CREATE_DOCUMENTS); + $documents = []; + for ($i = 1; $i <= 10; $i++) { + $documents[] = ['$id' => 'doc' . $i, 'name' => 'Doc #' . $i]; + } + + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documents' => $documents, + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['collectionsCreateDocuments']['documents']); + + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'projectId' => $projectId, + ]; + } + + /** + * @depends testBulkCreateDocuments + */ + public function testBulkUpdateDocuments(array $data): array + { + $userId = $this->getUser()['$id']; + $permissions = [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ]; + + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + ], $this->getHeaders()); + + $query = $this->getQuery(self::$UPDATE_DOCUMENTS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'collectionId' => $data['collectionId'], + 'data' => [ + 'name' => 'Docs Updated', + '$permissions' => $permissions, + ], + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['collectionsUpdateDocuments']['documents']); + + return $data; + } + + /** + * @depends testBulkUpdateDocuments + */ + public function testBulkUpsertDocuments(array $data): array + { + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + ], $this->getHeaders()); + + // Upsert: Update one, insert one + $query = $this->getQuery(self::$UPSERT_DOCUMENTS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'collectionId' => $data['collectionId'], + 'documents' => [ + ['$id' => 'doc10', 'name' => 'Doc #1000'], + ['name' => 'Doc #11'], + ], + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(2, $res['body']['data']['collectionsUpsertDocuments']['documents']); + + return $data; + } + + /** + * @depends testBulkUpsertDocuments + */ + public function testBulkDeleteDocuments(array $data): array + { + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + ], $this->getHeaders()); + + $query = $this->getQuery(self::$DELETE_DOCUMENTS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'collectionId' => $data['collectionId'], + ], + ]; + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(11, $res['body']['data']['collectionsDeleteDocuments']['documents']); + + return $data; + } } From 9cfff792fdcc6617edf4445a2152528ff87529d4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 14:13:26 +0530 Subject: [PATCH 146/173] add: graphql tests for bulk apis to server tests. --- .../GraphQL/Tables/DatabaseServerTest.php | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php index af72bb8a9f..61cb57911a 100644 --- a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php @@ -12,6 +12,7 @@ use Utopia\Database\Database; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Query; class DatabaseServerTest extends Scope { @@ -1465,4 +1466,282 @@ class DatabaseServerTest extends Scope $this->assertIsNotArray($database['body']); $this->assertEquals(204, $database['headers']['status-code']); } + + /** + * @throws Exception + */ + public function testBulkCreate(): array + { + $project = $this->getProject(); + $projectId = $project['$id']; + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()); + + // Step 1: Create database + $query = $this->getQuery(self::$CREATE_DATABASE); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => 'bulk', + 'name' => 'Bulk', + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $databaseId = $res['body']['data']['databasesCreate']['_id']; + + // Step 2: Create table + $query = $this->getQuery(self::$CREATE_TABLE); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'tableId' => 'operations', + 'name' => 'Operations', + 'rowSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $tableId = $res['body']['data']['databasesCreateTable']['_id']; + + // Step 3: Create column + $query = $this->getQuery(self::$CREATE_STRING_COLUMN); + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + sleep(1); + + // Step 4: Create rows + $query = $this->getQuery(self::$CREATE_ROWS); + $rows = []; + for ($i = 1; $i <= 10; $i++) { + $rows[] = ['$id' => 'row' . $i, 'name' => 'Row #' . $i]; + } + + $payload['query'] = $query; + $payload['variables'] = [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rows' => $rows, + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['tablesCreateRows']['rows']); + + return compact('databaseId', 'tableId', 'projectId'); + } + + /** + * @depends testBulkCreate + */ + public function testBulkUpdate(array $data): array + { + $userId = $this->getUser()['$id']; + $permissions = [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ]; + + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + ], $this->getHeaders()); + + // Step 1: Bulk update rows + $query = $this->getQuery(self::$UPDATE_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'data' => [ + 'name' => 'Rows Updated', + '$permissions' => $permissions, + ], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + $this->assertCount(10, $res['body']['data']['tablesUpdateRows']['rows']); + + // Step 2: Fetch and validate updated rows + $query = $this->getQuery(self::$GET_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'queries' => [Query::equal('name', ['Rows Updated'])->toString()], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertEquals(200, $res['headers']['status-code']); + + $fetched = $res['body']['data']['tablesListRows']; + $this->assertEquals(10, $fetched['total']); + + foreach ($fetched['rows'] as $row) { + $this->assertEquals($permissions, $row['_permissions']); + $this->assertEquals($data['tableId'], $row['_tableId']); + $this->assertEquals($data['databaseId'], $row['_databaseId']); + $this->assertEquals('Rows Updated', json_decode($row['data'], true)['name']); + } + + return $data; + } + + /** + * @depends testBulkCreate + */ + public function testBulkUpsert(array $data): array + { + $userId = $this->getUser()['$id']; + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + ], $this->getHeaders()); + + $permissions = [ + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ]; + + // Step 1: Mutate row 10 and add row 11 + $query = $this->getQuery(self::$UPSERT_ROWS); + $upsertPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'rows' => [ + [ + '$id' => 'row10', + 'name' => 'Row #1000', + ], + [ + 'name' => 'Row #11', + ], + ], + ], + ]; + + $response = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $upsertPayload); + $this->assertArrayNotHasKey('errors', $response['body']); + + $rows = $response['body']['data']['tablesUpsertRows']['rows']; + $this->assertCount(2, $rows); + + $rowMap = []; + foreach ($rows as $row) { + $decoded = json_decode($row['data'], true); + $rowMap[$decoded['name']] = $decoded; + } + + $this->assertArrayHasKey('Row #1000', $rowMap); + $this->assertArrayHasKey('Row #11', $rowMap); + + // Step 2: Fetch all rows and confirm count is now 11 + $query = $this->getQuery(self::$GET_ROWS); + $fetchPayload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $fetchPayload); + $this->assertEquals(200, $res['headers']['status-code']); + + $fetched = $res['body']['data']['tablesListRows']; + $this->assertEquals(11, $fetched['total']); + + // Step 3: Upsert row with new permissions using `tablesUpsertRow` + $query = $this->getQuery(self::$UPSERT_ROW); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + 'rowId' => 'row10', + 'data' => ['name' => 'Row #10 Patched'], + 'permissions' => $permissions, + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + + $updated = $res['body']['data']['tablesUpsertRow']; + $this->assertEquals('Row #10 Patched', json_decode($updated['data'], true)['name']); + $this->assertEquals($data['databaseId'], $updated['_databaseId']); + $this->assertEquals($data['tableId'], $updated['_tableId']); + + return $data; + } + + /** + * @depends testBulkUpsert + */ + public function testBulkDelete(array $data): array + { + $headers = array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + ], $this->getHeaders()); + + // Step 1: Perform bulk delete + $query = $this->getQuery(self::$DELETE_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertArrayNotHasKey('errors', $res['body']); + + $deleted = $res['body']['data']['tablesDeleteRows']['rows']; + $this->assertIsArray($deleted); + $this->assertCount(11, $deleted); + + // Step 2: Confirm deletion via refetch + $query = $this->getQuery(self::$GET_ROWS); + $payload = [ + 'query' => $query, + 'variables' => [ + 'databaseId' => $data['databaseId'], + 'tableId' => $data['tableId'], + ], + ]; + + $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); + $this->assertEquals(200, $res['headers']['status-code']); + $this->assertEquals(0, $res['body']['data']['tablesListRows']['total']); + + return $data; + } } From 8cbd06173db748191d4ee6f2794b5e7fbf1ad2eb Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 15:11:18 +0530 Subject: [PATCH 147/173] update: realtime channels checks for tables api. --- .../Services/Realtime/RealtimeCustomClientTest.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 7f5e2a9da8..6d6c0d18a4 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -81,11 +81,17 @@ class RealtimeCustomClientTest extends Scope 'files', 'files.1', 'collections', + 'tables', 'collections.1.documents', 'collections.2.documents', + 'tables.1.rows', + 'tables.2.rows', 'documents', + 'rows', 'collections.1.documents.1', 'collections.2.documents.2', + 'tables.1.rows.1', + 'tables.2.rows.2', ], $headers); $response = json_decode($client->receive(), true); @@ -95,17 +101,22 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals('connected', $response['type']); $this->assertNotEmpty($response['data']); $this->assertNotEmpty($response['data']['user']); - $this->assertCount(10, $response['data']['channels']); + $this->assertCount(16, $response['data']['channels']); $this->assertContains('account', $response['data']['channels']); $this->assertContains('account.' . $userId, $response['data']['channels']); $this->assertContains('files', $response['data']['channels']); $this->assertContains('files.1', $response['data']['channels']); $this->assertContains('collections', $response['data']['channels']); + $this->assertContains('tables', $response['data']['channels']); $this->assertContains('collections.1.documents', $response['data']['channels']); $this->assertContains('collections.2.documents', $response['data']['channels']); + $this->assertContains('tables.1.rows', $response['data']['channels']); + $this->assertContains('tables.2.rows', $response['data']['channels']); $this->assertContains('documents', $response['data']['channels']); $this->assertContains('collections.1.documents.1', $response['data']['channels']); $this->assertContains('collections.2.documents.2', $response['data']['channels']); + $this->assertContains('tables.1.rows.1', $response['data']['channels']); + $this->assertContains('tables.2.rows.2', $response['data']['channels']); $this->assertEquals($userId, $response['data']['user']['$id']); $client->close(); From 8d74083da3a6b214c557d22fed943d7859652299 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 15:18:21 +0530 Subject: [PATCH 148/173] remove: filter queries. --- .../Databases/Http/Databases/Collections/Attributes/XList.php | 4 +--- .../Databases/Http/Databases/Collections/Indexes/XList.php | 3 +-- .../Modules/Databases/Http/Databases/Collections/XList.php | 4 +--- .../Platform/Modules/Databases/Http/Databases/XList.php | 4 +--- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index b9f1450fa3..2509115e13 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -118,11 +118,9 @@ class XList extends Action $cursor->setValue($cursorDocument); } - $filterQueries = Query::groupByType($queries)['filters']; - try { $attributes = $dbForProject->find('attributes', $queries); - $total = $dbForProject->count('attributes', $filterQueries, APP_LIMIT_COUNT); + $total = $dbForProject->count('attributes', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index 13717034ce..dae25a3335 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -122,9 +122,8 @@ class XList extends Action $cursor->setValue($cursorDocument[0]); } - $filterQueries = Query::groupByType($queries)['filters']; try { - $total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT); + $total = $dbForProject->count('indexes', $queries, APP_LIMIT_COUNT); $indexes = $dbForProject->find('indexes', $queries); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index 06a6d2cd39..d3ddb9301f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -108,11 +108,9 @@ class XList extends Action $cursor->setValue($cursorDocument); } - $filterQueries = Query::groupByType($queries)['filters']; - try { $collections = $dbForProject->find('database_' . $database->getSequence(), $queries); - $total = $dbForProject->count('database_' . $database->getSequence(), $filterQueries, APP_LIMIT_COUNT); + $total = $dbForProject->count('database_' . $database->getSequence(), $queries, APP_LIMIT_COUNT); } catch (OrderException) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL); } catch (QueryException) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php index dcf7e2d724..7179be90ec 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php @@ -92,11 +92,9 @@ class XList extends Action $cursor->setValue($cursorDocument); } - $filterQueries = Query::groupByType($queries)['filters']; - try { $databases = $dbForProject->find('databases', $queries); - $total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT); + $total = $dbForProject->count('databases', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null."); } catch (QueryException) { From e236aac0ea4388e61e30e9c65af4a87c6f7b5d07 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 09:57:35 +0530 Subject: [PATCH 149/173] =?UTF-8?q?fix:=20events=20logic=20=F0=9F=98=AE?= =?UTF-8?q?=E2=80=8D=F0=9F=92=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Http/Databases/Collections/Documents/Create.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index e68a27ba34..6005495a68 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -56,7 +56,6 @@ class Create extends Action ->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents') ->desc('Create document') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'document.create') @@ -418,12 +417,6 @@ class Create extends Action $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); - // BIG TODO: @itznotabug - need to check what to do for bulk api because there's not just one `[document/rowId]`. - $queueForEvents - ->setParam('documentId', $documents[0]->getId()) - ->setParam('rowId', $documents[0]->getId()) - // TODO: @itznotabug - check if the events mirroring works here! - ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); if ($isBulk) { $response->dynamic(new Document([ @@ -434,6 +427,11 @@ class Create extends Action return; } + $queueForEvents + ->setParam('documentId', $documents[0]->getId()) + ->setParam('rowId', $documents[0]->getId()) + ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); + $response->dynamic( $documents[0], $this->getResponseModel() From 1337b759deffd552c05c28e80c82f23ff3b6720b Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 10:15:45 +0530 Subject: [PATCH 150/173] fix: remove event for bulk api support. --- .../Modules/Databases/Http/Databases/Tables/Rows/Create.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 881d2b28ed..03e5548ddb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -47,7 +47,6 @@ class Create extends DocumentCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Create row') ->groups(['api', 'database']) - ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create') ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.create') From 1fbbbee31012d5d0a430f24b6f522372af957c10 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 15:01:13 +0530 Subject: [PATCH 151/173] update: add cached logic to `processDocument`. --- .../Collections/Documents/Action.php | 93 +++++++++++++++++++ .../Collections/Documents/Create.php | 39 +------- .../Collections/Documents/Delete.php | 38 +------- .../Databases/Collections/Documents/Get.php | 52 ++--------- .../Collections/Documents/Update.php | 37 +------- .../Collections/Documents/Upsert.php | 37 +------- .../Databases/Collections/Documents/XList.php | 66 ++----------- 7 files changed, 126 insertions(+), 236 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index c0e71d3730..32e26bb344 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -4,6 +4,9 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documen use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Context; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action as UtopiaAction; abstract class Action extends UtopiaAction @@ -193,4 +196,94 @@ abstract class Action extends UtopiaAction { return $this->isCollectionsAPI() ? 'collection' : 'table'; } + + /** + * Resolves relationships in a document and attaches metadata. + */ + final protected function resolveDocumentRelations(Document $document, Document $collection, array &$context): bool + { + /* @type Document $database */ + $database = $context['database']; + + /* @type Database $dbForProject */ + $dbForProject = $context['dbForProject']; + + /* remove `$collection` if needed */ + $removeCollection = $context['removeCollection'] ?? false; + + /* count operations and use `continue` inside loop */ + $trackOperations = array_key_exists('trackOperations', $context); + + if (!$trackOperations) { + $context['operations'] ??= 0; + } elseif ($document->isEmpty()) { + return false; + } + + $operations = &$context['operations']; + $collectionsCache = &$context['collectionsCache']; + + $operations++; + $collectionId = $collection->getId(); + + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collectionId); + + if ($removeCollection) { + $document->removeAttribute('$collection'); + } + + $relationships = $collectionsCache[$collectionId] ??= \array_filter( + $collection->getAttribute('attributes', []), + fn ($attr) => $attr->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $key = $relationship->getAttribute('key'); + $related = $document->getAttribute($key); + + if (empty($related)) { + if (\in_array(\gettype($related), ['array', 'object'])) { + $operations++; + } + continue; + } + + $relations = \is_array($related) ? $related : [$related]; + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + + if (!isset($collectionsCache[$relatedCollectionId])) { + $relatedCollectionDoc = Authorization::skip( + fn () => $dbForProject->getDocument( + 'database_' . $database->getSequence(), + $relatedCollectionId + ) + ); + + $collectionsCache[$relatedCollectionId] = \array_filter( + $relatedCollectionDoc->getAttribute('attributes', []), + fn ($attr) => $attr->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + } + + foreach ($relations as $index => $relation) { + if ($relation instanceof Document) { + $relatedCollection = new Document([ + '$id' => $relatedCollectionId, + 'attributes' => $collectionsCache[$relatedCollectionId], + ]); + + $this->resolveDocumentRelations(document: $relation, collection: $relatedCollection, context: $context); + } + } + + if (\is_array($related)) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } elseif (empty($relations)) { + $document->setAttribute($relationship->getAttribute('key'), null); + } + } + + return true; + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 6005495a68..47c6acf0b7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -373,42 +373,13 @@ class Create extends Action ->setParam('tableId', $collection->getId()) ->setContext($this->getCollectionsEventsContext(), $collection); - // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $table, Document $document) use (&$processDocument, $dbForProject, $database) { - $document->removeAttribute('$collection'); - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); - } - } - } - }; + $collectionsCache = []; + $removeCollection = true; + $context = compact('database', 'dbForProject', 'collectionsCache', 'removeCollection'); foreach ($documents as $document) { - $processDocument($collection, $document); + // Add $collectionId and $databaseId for all documents + $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); } $queueForStatsUsage diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index dc307071e4..9f1f27b1f8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -12,7 +12,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Validator\Authorization; @@ -115,40 +114,11 @@ class Delete extends Action throw new Exception($this->getRestrictedException()); } + $collectionsCache = []; + $context = compact('database', 'dbForProject', 'collectionsCache'); + // Add $collection and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); - - $relationships = \array_filter( - $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); - } - } - } - }; - - $processDocument($collection, $document); + $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index 3f7f74ee75..bf1acb8086 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -11,7 +11,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; @@ -101,52 +100,15 @@ class Get extends Action } $operations = 0; + $collectionsCache = []; + $trackOperations = true; + $context = compact('database', 'dbForProject', 'operations', 'trackOperations', 'collectionsCache'); - // Add $collectionId and $databaseId for all rows - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations) { - if ($document->isEmpty()) { - return; - } + // Add $collectionId and $databaseId for all documents + $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); - $operations++; - - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); - - $relationships = \array_filter( - $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { - $operations++; - } - - continue; - } - - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); - } - } - } - }; - - $processDocument($collection, $document); + // get updated from the context + $operations = $context['operations']; $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 3bc97e817b..b946731c7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -247,40 +247,11 @@ class Update extends Action throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } + $collectionsCache = []; + $context = compact('database', 'dbForProject', 'collectionsCache'); + // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $table, Document $row) use (&$processDocument, $dbForProject, $database) { - $row->setAttribute('$databaseId', $database->getId()); - $row->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $row->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); - } - } - } - }; - - $processDocument($collection, $document); + $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); $response->dynamic($document, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index ebe30f6970..d27ff632b8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -235,41 +235,12 @@ class Upsert extends Action throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } + $collectionsCache = []; $document = $upserted[0]; + $context = compact('database', 'dbForProject', 'collectionsCache'); + // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $table, Document $document) use (&$processDocument, $dbForProject, $database) { - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $table->getId()); - - $relationships = \array_filter( - $table->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) - ); - - foreach ($related as $relation) { - if ($relation instanceof Document) { - $processDocument($relatedCollection, $relation); - } - } - } - }; - - $processDocument($collection, $document); + $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 0012b8339a..b87efe42de 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -130,67 +130,19 @@ class XList extends Action } $operations = 0; + $collectionsCache = []; + $trackOperations = true; + $removeCollection = true; + $context = compact('database', 'dbForProject', 'operations', 'collectionsCache', 'removeCollection', 'trackOperations'); // Add $collectionId and $databaseId for all documents - $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations): bool { - if ($document->isEmpty()) { - return false; - } - - $operations++; - - $document->removeAttribute('$collection'); - $document->setAttribute('$databaseId', $database->getId()); - $document->setAttribute('$collectionId', $collection->getId()); - - $relationships = \array_filter( - $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { - $operations++; - } - - continue; - } - - if (!\is_array($related)) { - $relations = [$related]; - } else { - $relations = $related; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - // todo: Use local cache for this getDocument - $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)); - - foreach ($relations as $index => $doc) { - if ($doc instanceof Document) { - if (!$processDocument($relatedCollection, $doc)) { - unset($relations[$index]); - } - } - } - - if (\is_array($related)) { - $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); - } elseif (empty($relations)) { - $document->setAttribute($relationship->getAttribute('key'), null); - } - } - - return true; - }); - - foreach ($documents as $row) { - $processDocument($collection, $row); + foreach ($documents as $document) { + $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); } + // get updated from the context + $operations = $context['operations']; + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); From 16a87535097292cad4e395def4a3ba467b89be38 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 15:03:11 +0530 Subject: [PATCH 152/173] update: comment. --- .../Databases/Http/Databases/Collections/Documents/Action.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 32e26bb344..d5e7d84695 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -211,7 +211,7 @@ abstract class Action extends UtopiaAction /* remove `$collection` if needed */ $removeCollection = $context['removeCollection'] ?? false; - /* count operations and use `continue` inside loop */ + /* count operations inside loop */ $trackOperations = array_key_exists('trackOperations', $context); if (!$trackOperations) { From d6bf748a079682546a19efa54945cd110f0aee8d Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 18 Jun 2025 12:00:19 +0530 Subject: [PATCH 153/173] address comments. --- .../Collections/Documents/Action.php | 51 +++++++++---------- .../Collections/Documents/Create.php | 12 +++-- .../Collections/Documents/Delete.php | 11 ++-- .../Databases/Collections/Documents/Get.php | 16 +++--- .../Collections/Documents/Update.php | 11 ++-- .../Collections/Documents/Upsert.php | 11 ++-- .../Databases/Collections/Documents/XList.php | 17 +++---- 7 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index d5e7d84695..52dfc62f50 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -200,39 +200,31 @@ abstract class Action extends UtopiaAction /** * Resolves relationships in a document and attaches metadata. */ - final protected function resolveDocumentRelations(Document $document, Document $collection, array &$context): bool - { - /* @type Document $database */ - $database = $context['database']; + final protected function processDocument( + /* database */ + Document $database, + Document $collection, + Document $document, + Database $dbForProject, - /* @type Database $dbForProject */ - $dbForProject = $context['dbForProject']; + /* options */ + array &$collectionsCache, + ?int &$operations = null, + ): bool { - /* remove `$collection` if needed */ - $removeCollection = $context['removeCollection'] ?? false; - - /* count operations inside loop */ - $trackOperations = array_key_exists('trackOperations', $context); - - if (!$trackOperations) { - $context['operations'] ??= 0; - } elseif ($document->isEmpty()) { + if ($operations !== null && $document->isEmpty()) { return false; } - $operations = &$context['operations']; - $collectionsCache = &$context['collectionsCache']; + if ($operations !== null) { + $operations++; + } - $operations++; $collectionId = $collection->getId(); - + $document->removeAttribute('$collection'); $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collectionId); - if ($removeCollection) { - $document->removeAttribute('$collection'); - } - $relationships = $collectionsCache[$collectionId] ??= \array_filter( $collection->getAttribute('attributes', []), fn ($attr) => $attr->getAttribute('type') === Database::VAR_RELATIONSHIP @@ -243,7 +235,7 @@ abstract class Action extends UtopiaAction $related = $document->getAttribute($key); if (empty($related)) { - if (\in_array(\gettype($related), ['array', 'object'])) { + if (\in_array(\gettype($related), ['array', 'object']) && $operations !== null) { $operations++; } continue; @@ -266,14 +258,21 @@ abstract class Action extends UtopiaAction ); } - foreach ($relations as $index => $relation) { + foreach ($relations as $relation) { if ($relation instanceof Document) { $relatedCollection = new Document([ '$id' => $relatedCollectionId, 'attributes' => $collectionsCache[$relatedCollectionId], ]); - $this->resolveDocumentRelations(document: $relation, collection: $relatedCollection, context: $context); + $this->processDocument( + database: $database, + collection: $relatedCollection, + document: $relation, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + operations: $operations + ); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 47c6acf0b7..ebab4a7ece 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -374,12 +374,14 @@ class Create extends Action ->setContext($this->getCollectionsEventsContext(), $collection); $collectionsCache = []; - $removeCollection = true; - $context = compact('database', 'dbForProject', 'collectionsCache', 'removeCollection'); - foreach ($documents as $document) { - // Add $collectionId and $databaseId for all documents - $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); + $this->processDocument( + database: $database, + collection: $collection, + document: $document, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + ); } $queueForStatsUsage diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 9f1f27b1f8..d8ec82910c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -115,10 +115,13 @@ class Delete extends Action } $collectionsCache = []; - $context = compact('database', 'dbForProject', 'collectionsCache'); - - // Add $collection and $databaseId for all documents - $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); + $this->processDocument( + database: $database, + collection: $collection, + document: $document, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + ); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index bf1acb8086..fe40a2412f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -101,14 +101,14 @@ class Get extends Action $operations = 0; $collectionsCache = []; - $trackOperations = true; - $context = compact('database', 'dbForProject', 'operations', 'trackOperations', 'collectionsCache'); - - // Add $collectionId and $databaseId for all documents - $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); - - // get updated from the context - $operations = $context['operations']; + $this->processDocument( + database: $database, + collection: $collection, + document: $document, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + operations: $operations + ); $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index b946731c7a..c39eeea707 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -248,10 +248,13 @@ class Update extends Action } $collectionsCache = []; - $context = compact('database', 'dbForProject', 'collectionsCache'); - - // Add $collectionId and $databaseId for all documents - $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); + $this->processDocument( + database: $database, + collection: $collection, + document: $document, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + ); $response->dynamic($document, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index d27ff632b8..fa632c7b2b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -237,10 +237,13 @@ class Upsert extends Action $collectionsCache = []; $document = $upserted[0]; - $context = compact('database', 'dbForProject', 'collectionsCache'); - - // Add $collectionId and $databaseId for all documents - $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); + $this->processDocument( + database: $database, + collection: $collection, + document: $document, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + ); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index b87efe42de..9aad8f4b77 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -131,18 +131,17 @@ class XList extends Action $operations = 0; $collectionsCache = []; - $trackOperations = true; - $removeCollection = true; - $context = compact('database', 'dbForProject', 'operations', 'collectionsCache', 'removeCollection', 'trackOperations'); - - // Add $collectionId and $databaseId for all documents foreach ($documents as $document) { - $this->resolveDocumentRelations(document: $document, collection: $collection, context: $context); + $this->processDocument( + database: $database, + collection: $collection, + document: $document, + dbForProject: $dbForProject, + collectionsCache: $collectionsCache, + operations: $operations, + ); } - // get updated from the context - $operations = $context['operations']; - $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); From d69189bd55317fe5b31bce211669e24be6991c74 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 18 Jun 2025 12:02:08 +0530 Subject: [PATCH 154/173] ci: empty commit From c93d92abef22927abdd3a7be942f1c2ecddbabc9 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 10:52:15 +0530 Subject: [PATCH 155/173] rename: tests from `collections` to `legacy`. --- .github/workflows/tests.yml | 4 ++-- .../Databases/{Collections => Legacy}/DatabasesBase.php | 2 +- .../{Collections => Legacy}/DatabasesConsoleClientTest.php | 2 +- .../{Collections => Legacy}/DatabasesCustomClientTest.php | 2 +- .../{Collections => Legacy}/DatabasesCustomServerTest.php | 2 +- .../{Collections => Legacy}/DatabasesPermissionsGuestTest.php | 2 +- .../DatabasesPermissionsMemberTest.php | 2 +- .../{Collections => Legacy}/DatabasesPermissionsScope.php | 2 +- .../{Collections => Legacy}/DatabasesPermissionsTeamTest.php | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesBase.php (99%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesConsoleClientTest.php (99%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesCustomClientTest.php (99%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesCustomServerTest.php (99%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesPermissionsGuestTest.php (99%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesPermissionsMemberTest.php (99%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesPermissionsScope.php (98%) rename tests/e2e/Services/Databases/{Collections => Legacy}/DatabasesPermissionsTeamTest.php (99%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cf1e55f7ec..766b295fff 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -145,7 +145,7 @@ jobs: Account, Avatars, Console, - Databases/Collections, + Databases/Legacy, Databases/Tables, Functions, FunctionsSchedule, @@ -214,7 +214,7 @@ jobs: Account, Avatars, Console, - Databases/Collections, + Databases/Legacy, Databases/Tables, Functions, FunctionsSchedule, diff --git a/tests/e2e/Services/Databases/Collections/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php similarity index 99% rename from tests/e2e/Services/Databases/Collections/DatabasesBase.php rename to tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 6b0aca1512..69b24d5052 100644 --- a/tests/e2e/Services/Databases/Collections/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -1,6 +1,6 @@ Date: Thu, 19 Jun 2025 10:54:56 +0530 Subject: [PATCH 156/173] change: namespace. --- .../Databases/Http/Databases/Collections/Attributes/Action.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Action.php | 2 +- .../Databases/Http/Databases/Collections/Indexes/Action.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 2ee265a18d..b115edbee8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -83,7 +83,7 @@ abstract class Action extends UtopiaAction */ final protected function getSdkNamespace(): string { - return $this->isCollectionsAPI() ? 'collections' : 'tables'; + return $this->isCollectionsAPI() ? 'databases' : 'tables'; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 52dfc62f50..bd92e233f1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -78,7 +78,7 @@ abstract class Action extends UtopiaAction */ final protected function getSdkNamespace(): string { - return $this->isCollectionsAPI() ? 'collections' : 'tables'; + return $this->isCollectionsAPI() ? 'databases' : 'tables'; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index 4a40ea6b5f..7013259fa7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -71,7 +71,7 @@ abstract class Action extends UtopiaAction */ final protected function getSdkNamespace(): string { - return $this->isCollectionsAPI() ? 'collections' : 'tables'; + return $this->isCollectionsAPI() ? 'databases' : 'tables'; } /** From e5caa856290a5403f0deef25b22edc4193dbe6dc Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 11:02:31 +0530 Subject: [PATCH 157/173] update: init with module structure. --- app/controllers/general.php | 14 -------- .../Modules/Databases/Http/Init/Timeout.php | 35 +++++++++++++++++++ .../Modules/Databases/Services/Http.php | 2 ++ 3 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Init/Timeout.php diff --git a/app/controllers/general.php b/app/controllers/general.php index 1872ba82e3..ba5b409a2b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1582,20 +1582,6 @@ foreach (Config::getParam('services', []) as $service) { } } -// legacy controller init hooks as the endpoint controller logic is now moved to a module structure. -// 1. databases -App::init() - ->groups(['api', 'database']) - ->inject('request') - ->inject('dbForProject') - ->action(function (Request $request, Database $dbForProject) { - $timeout = \intval($request->getHeader('x-appwrite-timeout')); - - if (!empty($timeout) && App::isDevelopment()) { - $dbForProject->setTimeout($timeout); - } - }); - // Check for any errors found while we were initialising the SDK Methods. if (!empty(Method::getErrors())) { throw new \Exception('Errors found during SDK initialization:' . PHP_EOL . implode(PHP_EOL, Method::getErrors())); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Init/Timeout.php b/src/Appwrite/Platform/Modules/Databases/Http/Init/Timeout.php new file mode 100644 index 0000000000..19e202981b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Init/Timeout.php @@ -0,0 +1,35 @@ +setType(Action::TYPE_INIT) + ->groups(['api', 'database']) + ->inject('request') + ->inject('dbForProject') + ->callback(function (Request $request, Database $dbForProject) { + $timeout = \intval($request->getHeader('x-appwrite-timeout')); + + if (!empty($timeout) && App::isDevelopment()) { + $dbForProject->setTimeout($timeout); + } + }); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index 6c1361b0b1..a877c89345 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Services; +use Appwrite\Platform\Modules\Databases\Http\Init\Timeout; use Appwrite\Platform\Modules\Databases\Services\Registry\Collections as CollectionsRegistry; use Appwrite\Platform\Modules\Databases\Services\Registry\Databases as DatabasesRegistry; use Appwrite\Platform\Modules\Databases\Services\Registry\Tables as TablesRegistry; @@ -14,6 +15,7 @@ class Http extends Service $this->type = Service::TYPE_HTTP; foreach ([ + Timeout::class, DatabasesRegistry::class, CollectionsRegistry::class, TablesRegistry::class, From 1d23e2ec127bf53951f5718b2c4401a89887049a Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 14:31:54 +0530 Subject: [PATCH 158/173] remove: `use HTTP`. update: inline context logic. --- .../Platform/Modules/Databases/Constants.php | 24 +++++++++++++++++ .../Platform/Modules/Databases/Context.php | 26 ------------------- .../Http/Databases/Collections/Action.php | 19 +++++--------- .../Collections/Attributes/Action.php | 18 +++++-------- .../Collections/Attributes/Boolean/Create.php | 3 --- .../Collections/Attributes/Boolean/Update.php | 3 --- .../Attributes/Datetime/Create.php | 3 --- .../Attributes/Datetime/Update.php | 3 --- .../Collections/Attributes/Delete.php | 3 --- .../Collections/Attributes/Email/Create.php | 3 --- .../Collections/Attributes/Email/Update.php | 3 --- .../Collections/Attributes/Enum/Create.php | 3 --- .../Collections/Attributes/Enum/Update.php | 3 --- .../Collections/Attributes/Float/Create.php | 3 --- .../Collections/Attributes/Float/Update.php | 3 --- .../Databases/Collections/Attributes/Get.php | 3 --- .../Collections/Attributes/IP/Create.php | 3 --- .../Collections/Attributes/IP/Update.php | 3 --- .../Collections/Attributes/Integer/Create.php | 3 --- .../Collections/Attributes/Integer/Update.php | 3 --- .../Attributes/Relationship/Create.php | 3 --- .../Attributes/Relationship/Update.php | 3 --- .../Collections/Attributes/String/Create.php | 3 --- .../Collections/Attributes/String/Update.php | 3 --- .../Collections/Attributes/URL/Create.php | 3 --- .../Collections/Attributes/URL/Update.php | 3 --- .../Collections/Attributes/XList.php | 3 --- .../Http/Databases/Collections/Create.php | 3 --- .../Http/Databases/Collections/Delete.php | 3 --- .../Collections/Documents/Action.php | 18 +++++-------- .../Documents/Attribute/Decrement.php | 3 --- .../Documents/Attribute/Increment.php | 3 --- .../Collections/Documents/Bulk/Delete.php | 3 --- .../Collections/Documents/Bulk/Update.php | 3 --- .../Collections/Documents/Bulk/Upsert.php | 3 --- .../Collections/Documents/Create.php | 3 --- .../Collections/Documents/Delete.php | 3 --- .../Databases/Collections/Documents/Get.php | 3 --- .../Collections/Documents/Logs/XList.php | 3 --- .../Collections/Documents/Update.php | 3 --- .../Collections/Documents/Upsert.php | 3 --- .../Databases/Collections/Documents/XList.php | 3 --- .../Http/Databases/Collections/Get.php | 3 --- .../Databases/Collections/Indexes/Action.php | 22 +++++----------- .../Databases/Collections/Indexes/Create.php | 3 --- .../Databases/Collections/Indexes/Delete.php | 3 --- .../Databases/Collections/Indexes/Get.php | 3 --- .../Databases/Collections/Indexes/XList.php | 3 --- .../Http/Databases/Collections/Logs/XList.php | 3 --- .../Http/Databases/Collections/Update.php | 3 --- .../Http/Databases/Collections/Usage/Get.php | 3 --- .../Http/Databases/Collections/XList.php | 8 +++--- .../Databases/Http/Databases/Create.php | 3 --- .../Databases/Http/Databases/Delete.php | 3 --- .../Modules/Databases/Http/Databases/Get.php | 3 --- .../Databases/Http/Databases/Logs/XList.php | 3 --- .../Tables/Columns/Boolean/Create.php | 6 ----- .../Tables/Columns/Boolean/Update.php | 6 ----- .../Tables/Columns/Datetime/Create.php | 6 ----- .../Tables/Columns/Datetime/Update.php | 6 ----- .../Http/Databases/Tables/Columns/Delete.php | 6 ----- .../Databases/Tables/Columns/Email/Create.php | 6 ----- .../Databases/Tables/Columns/Email/Update.php | 6 ----- .../Databases/Tables/Columns/Enum/Create.php | 6 ----- .../Databases/Tables/Columns/Enum/Update.php | 6 ----- .../Databases/Tables/Columns/Float/Create.php | 6 ----- .../Databases/Tables/Columns/Float/Update.php | 6 ----- .../Http/Databases/Tables/Columns/Get.php | 6 ----- .../Databases/Tables/Columns/IP/Create.php | 6 ----- .../Databases/Tables/Columns/IP/Update.php | 6 ----- .../Tables/Columns/Integer/Create.php | 6 ----- .../Tables/Columns/Integer/Update.php | 6 ----- .../Tables/Columns/Relationship/Create.php | 6 ----- .../Tables/Columns/Relationship/Update.php | 6 ----- .../Tables/Columns/String/Create.php | 6 ----- .../Tables/Columns/String/Update.php | 6 ----- .../Databases/Tables/Columns/URL/Create.php | 6 ----- .../Databases/Tables/Columns/URL/Update.php | 6 ----- .../Http/Databases/Tables/Columns/XList.php | 6 ----- .../Http/Databases/Tables/Create.php | 6 ----- .../Http/Databases/Tables/Delete.php | 6 ----- .../Databases/Http/Databases/Tables/Get.php | 6 ----- .../Http/Databases/Tables/Indexes/Create.php | 6 ----- .../Http/Databases/Tables/Indexes/Delete.php | 6 ----- .../Http/Databases/Tables/Indexes/Get.php | 6 ----- .../Http/Databases/Tables/Indexes/XList.php | 6 ----- .../Http/Databases/Tables/Logs/XList.php | 6 ----- .../Databases/Tables/Rows/Bulk/Delete.php | 6 ----- .../Databases/Tables/Rows/Bulk/Update.php | 6 ----- .../Databases/Tables/Rows/Bulk/Upsert.php | 6 ----- .../Tables/Rows/Column/Decrement.php | 6 ----- .../Tables/Rows/Column/Increment.php | 6 ----- .../Http/Databases/Tables/Rows/Create.php | 6 ----- .../Http/Databases/Tables/Rows/Delete.php | 6 ----- .../Http/Databases/Tables/Rows/Get.php | 6 ----- .../Http/Databases/Tables/Rows/Logs/XList.php | 6 ----- .../Http/Databases/Tables/Rows/Update.php | 6 ----- .../Http/Databases/Tables/Rows/Upsert.php | 6 ----- .../Http/Databases/Tables/Rows/XList.php | 6 ----- .../Http/Databases/Tables/Update.php | 6 ----- .../Http/Databases/Tables/Usage/Get.php | 6 ----- .../Databases/Http/Databases/Tables/XList.php | 6 ----- .../Databases/Http/Databases/Update.php | 3 --- .../Databases/Http/Databases/Usage/Get.php | 3 --- .../Databases/Http/Databases/Usage/XList.php | 3 --- .../Databases/Http/Databases/XList.php | 3 --- .../Platform/Modules/Databases/Module.php | 2 ++ .../Modules/Databases/Services/Http.php | 4 ++- .../Services/Registry/Collections.php | 2 ++ 109 files changed, 62 insertions(+), 516 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Constants.php delete mode 100644 src/Appwrite/Platform/Modules/Databases/Context.php diff --git a/src/Appwrite/Platform/Modules/Databases/Constants.php b/src/Appwrite/Platform/Modules/Databases/Constants.php new file mode 100644 index 0000000000..cfc297c3f4 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Constants.php @@ -0,0 +1,24 @@ +context = TABLES; } - $this->context = $context; + return parent::setHttpPath($path); } /** @@ -53,7 +48,7 @@ abstract class Action extends UtopiaAction */ final protected function isCollectionsAPI(): bool { - return $this->getContext() === Context::DATABASE_COLLECTIONS; + return $this->getContext() === COLLECTIONS; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index b115edbee8..0a977bce32 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attribu use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response as UtopiaResponse; use Throwable; @@ -29,25 +28,20 @@ abstract class Action extends UtopiaAction /** * @var string|null The current context (either 'column' or 'attribute') */ - private ?string $context = Context::DATABASE_ATTRIBUTES; + private ?string $context = ATTRIBUTES; /** * Get the correct response model. */ abstract protected function getResponseModel(): string|array; - /** - * Set the context to either `column` or `attribute`. - * - * @throws \InvalidArgumentException If the context is invalid. - */ - final protected function setContext(string $context): void + public function setHttpPath(string $path): UtopiaAction { - if (!\in_array($context, [Context::DATABASE_COLUMNS, Context::DATABASE_ATTRIBUTES], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Use `Context::DATABASE_COLUMNS` or `Context::DATABASE_ATTRIBUTES`"); + if (str_contains($path, '/:databaseId/tables')) { + $this->context = COLUMNS; } - $this->context = $context; + return parent::setHttpPath($path); } /** @@ -65,7 +59,7 @@ abstract class Action extends UtopiaAction { // columns in tables context // attributes in collections context - return $this->getContext() === Context::DATABASE_ATTRIBUTES; + return $this->getContext() === ATTRIBUTES; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php index b1fddc0371..c38ef8d703 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php @@ -13,14 +13,11 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createBooleanAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php index 7aa2360fb8..9bb606edf5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php @@ -12,15 +12,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateBooleanAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php index fd54fe0843..ff8054abe4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php @@ -14,14 +14,11 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createDatetimeAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php index 17818fec2d..5f8909a294 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php @@ -13,15 +13,12 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateDatetimeAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 4582500091..5dba0860e2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -15,13 +15,10 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends Action { - use HTTP; - public static function getName(): string { return 'deleteAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php index 80f0270ea3..3c703f23a7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php @@ -14,14 +14,11 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createEmailAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php index 2f14d8a361..c8a34cf55c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php @@ -13,15 +13,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateEmailAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php index 006de12e91..99d540a4c2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php @@ -14,7 +14,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; @@ -22,8 +21,6 @@ use Utopia\Validator\Text; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createEnumAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php index c2f6d7696c..8624bde78d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; @@ -21,8 +20,6 @@ use Utopia\Validator\Text; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateEnumAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php index 35e9bb68a1..520366f689 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php @@ -14,7 +14,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; @@ -22,8 +21,6 @@ use Utopia\Validator\Range; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createFloatAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php index 2fdcf949f1..18a966d91d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; @@ -20,8 +19,6 @@ use Utopia\Validator\Nullable; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateFloatAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index d49639e049..d751e8def0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -11,13 +11,10 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php index c62e2add01..e46e508bc6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php @@ -13,15 +13,12 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createIpAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php index a3e1ea5a61..e9d9404c17 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; @@ -20,8 +19,6 @@ use Utopia\Validator\Nullable; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateIpAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php index 69272b6aea..e348e9a2a6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php @@ -14,7 +14,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; @@ -22,8 +21,6 @@ use Utopia\Validator\Range; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createIntegerAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php index 9c05382173..bbbf045115 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; @@ -20,8 +19,6 @@ use Utopia\Validator\Nullable; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateIntegerAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index fa8d0ad750..795ea445c8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -15,15 +15,12 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\WhiteList; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createRelationshipAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php index da2d0b9b08..7202a3e4e6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php @@ -12,14 +12,11 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateRelationshipAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php index cc989f6687..8ce8626f3a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php @@ -14,7 +14,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; use Utopia\Validator\Boolean; @@ -23,8 +22,6 @@ use Utopia\Validator\Text; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createStringAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php index b3f4cf3f03..b359b83eaa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; use Utopia\Validator\Boolean; @@ -22,8 +21,6 @@ use Utopia\Validator\Text; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateStringAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php index 2d72533cf1..27bc2d38c1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php @@ -13,15 +13,12 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\URL; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createUrlAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php index e5c4288d92..ae48c43653 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php @@ -12,7 +12,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; @@ -20,8 +19,6 @@ use Utopia\Validator\URL; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateUrlAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index 2509115e13..e09420d7ce 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -16,13 +16,10 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listAttributes'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index 5edce7715f..176c3ca853 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -21,15 +21,12 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createCollection'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php index b8080ab471..e4618a40b7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php @@ -13,13 +13,10 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends Action { - use HTTP; - public static function getName(): string { return 'deleteCollection'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index bd92e233f1..bfebca4cf7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Context; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -14,25 +13,20 @@ abstract class Action extends UtopiaAction /** * @var string|null The current context (either 'row' or 'document') */ - private ?string $context = Context::DATABASE_DOCUMENTS; + private ?string $context = DOCUMENTS; /** * Get the response model used in the SDK and HTTP responses. */ abstract protected function getResponseModel(): string; - /** - * Set the context to either `row` or `document`. - * - * @throws \InvalidArgumentException If the context is invalid. - */ - final protected function setContext(string $context): void + public function setHttpPath(string $path): UtopiaAction { - if (!\in_array($context, [Context::DATABASE_ROWS, Context::DATABASE_DOCUMENTS], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Use `Context::DATABASE_ROWS` or `Context::DATABASE_DOCUMENTS`"); + if (str_contains($path, '/:databaseId/tables')) { + $this->context = ROWS; } - $this->context = $context; + return parent::setHttpPath($path); } /** @@ -60,7 +54,7 @@ abstract class Action extends UtopiaAction { // rows in tables api context // documents in collections api context - return $this->getContext() === Context::DATABASE_DOCUMENTS; + return $this->getContext() === DOCUMENTS; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 9a8da5858c..40035ac302 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -19,14 +19,11 @@ use Utopia\Database\Exception\Type as TypeException; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Numeric; class Decrement extends Action { - use HTTP; - public static function getName(): string { return 'decrementDocumentAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index b65ca3c285..90694d1c69 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -19,14 +19,11 @@ use Utopia\Database\Exception\Type as TypeException; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Numeric; class Increment extends Action { - use HTTP; - public static function getName(): string { return 'incrementDocumentAttribute'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index bee3b35599..4ec852f7c5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -17,15 +17,12 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Query; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; class Delete extends Action { - use HTTP; - public static function getName(): string { return 'deleteDocuments'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 23c8d5d58e..d391c805fa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -19,7 +19,6 @@ use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Query; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; @@ -27,8 +26,6 @@ use Utopia\Validator\Text; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateDocuments'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 770f7a4d95..f4c8c2e6d2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -17,15 +17,12 @@ use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; class Upsert extends Action { - use HTTP; - public static function getName(): string { return 'upsertDocuments'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index ebab4a7ece..6c1fdae5e6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -25,15 +25,12 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createDocument'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index d8ec82910c..8c8cec2986 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -16,13 +16,10 @@ use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends Action { - use HTTP; - public static function getName(): string { return 'deleteDocument'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index fe40a2412f..7376fe770a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -15,15 +15,12 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getDocument'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 4f4dad9882..3fe25b9441 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -23,13 +23,10 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listDocumentLogs'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index c39eeea707..ce822ca713 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -23,14 +23,11 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateDocument'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index fa632c7b2b..7a077fed0d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -24,14 +24,11 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; class Upsert extends Action { - use HTTP; - public static function getName(): string { return 'upsertDocument'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 9aad8f4b77..2e76942db1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -18,15 +18,12 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listDocuments'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php index 0359025fc5..e35a924657 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php @@ -11,13 +11,10 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getCollection'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index 7013259fa7..33c2977be5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes; use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Databases\Context; use Utopia\Platform\Action as UtopiaAction; abstract class Action extends UtopiaAction @@ -11,25 +10,20 @@ abstract class Action extends UtopiaAction /** * The current API context (either 'columnIndex' or 'index'). */ - private ?string $context = Context::DATABASE_INDEX; + private ?string $context = INDEX; /** * Get the response model used in the SDK and HTTP responses. */ abstract protected function getResponseModel(): string; - /** - * Set the current API context. - * - * @param string $context Must be either `DATABASE_INDEX` or `DATABASE_COLUMN_INDEX`. - */ - final protected function setContext(string $context): void + public function setHttpPath(string $path): UtopiaAction { - if (!\in_array($context, [Context::DATABASE_INDEX, Context::DATABASE_COLUMN_INDEX], true)) { - throw new \InvalidArgumentException("Invalid context '$context'. Must be either `Context::DATABASE_COLUMN_INDEX` or `Context::DATABASE_INDEX`."); + if (str_contains($path, '/:databaseId/tables')) { + $this->context = COLUMN_INDEX; } - $this->context = $context; + return parent::setHttpPath($path); } /** @@ -37,9 +31,7 @@ abstract class Action extends UtopiaAction */ final protected function getParentContext(): string { - return $this->getContext() === Context::DATABASE_INDEX - ? Context::DATABASE_ATTRIBUTES - : Context::DATABASE_COLUMNS; + return $this->getContext() === INDEX ? ATTRIBUTES : COLUMNS; } /** @@ -55,7 +47,7 @@ abstract class Action extends UtopiaAction */ final protected function isCollectionsAPI(): bool { - return $this->getParentContext() === Context::DATABASE_ATTRIBUTES; + return $this->getParentContext() === ATTRIBUTES; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 9dbd7924c6..9003fbb4a9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -19,7 +19,6 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Index as IndexValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Integer; @@ -28,8 +27,6 @@ use Utopia\Validator\WhiteList; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createIndex'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index bb0b7bc4ca..701ab103ad 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -14,13 +14,10 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends Action { - use HTTP; - public static function getName(): string { return 'deleteIndex'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index e29bbf6647..c5b81fe231 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -12,13 +12,10 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getIndex'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index dae25a3335..36c52f711e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -17,13 +17,10 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listIndexes'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 2f9ac2c8c0..70bb14e9d8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -23,13 +23,10 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listCollectionLogs'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php index cc41d716d4..fd3b1e8bda 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php @@ -14,15 +14,12 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateCollection'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php index 0c565806cc..e5335d6abf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Usage/Get.php @@ -15,14 +15,11 @@ use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getCollectionUsage'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index d3ddb9301f..ae63a40e64 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -17,14 +17,11 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Text; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listCollections'; @@ -68,6 +65,11 @@ class XList extends Action public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void { + print_r(json_encode([ + 'context' => $this->getContext(), + 'model' => $this->getResponseModel() + ], JSON_PRETTY_PRINT)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php index 3b9efa2fca..38374402fb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Create.php @@ -19,15 +19,12 @@ use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; class Create extends Action { - use HTTP; - public static function getName(): string { return 'createDatabase'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php index b9b475f699..56cf1a4296 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php @@ -13,13 +13,10 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends Action { - use HTTP; - public static function getName(): string { return 'deleteDatabase'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php index c2c6a57da1..f478dc7917 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Get.php @@ -11,13 +11,10 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getDatabase'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php index a531110398..52f2ad93b6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php @@ -23,13 +23,10 @@ use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listDatabaseLogs'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php index b8675877f3..9e65e6e5b3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Boolean; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as BooleanCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,14 +9,11 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; class Create extends BooleanCreate { - use HTTP; - public static function getName(): string { return 'createBooleanColumn'; @@ -30,8 +26,6 @@ class Create extends BooleanCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php index 09d01cae21..f7eff81ae3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Boolean; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as BooleanUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,15 +10,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; class Update extends BooleanUpdate { - use HTTP; - public static function getName(): string { return 'updateBooleanColumn'; @@ -32,8 +28,6 @@ class Update extends BooleanUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php index 34ead7c33d..5bb83733a4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Datetime; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as DatetimeCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -12,14 +11,11 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; class Create extends DatetimeCreate { - use HTTP; - public static function getName(): string { return 'createDatetimeColumn'; @@ -32,8 +28,6 @@ class Create extends DatetimeCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php index 5b026cd373..6710f76aaa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Datetime; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Update as DatetimeUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -13,15 +12,12 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; class Update extends DatetimeUpdate { - use HTTP; - public static function getName(): string { return 'updateDatetimeColumn'; @@ -34,8 +30,6 @@ class Update extends DatetimeUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php index 567cb02169..b85248e8c3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Delete as AttributesDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,13 +10,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends AttributesDelete { - use HTTP; - public static function getName(): string { return 'deleteColumn'; @@ -31,8 +27,6 @@ class Delete extends AttributesDelete public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php index f5bb2f5220..6aade1aa50 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Email; use Appwrite\Network\Validator\Email; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Email\Create as EmailCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -11,14 +10,11 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; class Create extends EmailCreate { - use HTTP; - public static function getName(): string { return 'createEmailColumn'; @@ -31,8 +27,6 @@ class Create extends EmailCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php index faba9279b5..10213c87bb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Email; use Appwrite\Network\Validator\Email; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Email\Update as EmailUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,15 +11,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; class Update extends EmailUpdate { - use HTTP; - public static function getName(): string { return 'updateEmailColumn'; @@ -33,8 +29,6 @@ class Update extends EmailUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php index ec1d36a916..8e9a658aa8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Enum; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Enum\Create as EnumCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -11,7 +10,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; @@ -19,8 +17,6 @@ use Utopia\Validator\Text; class Create extends EnumCreate { - use HTTP; - public static function getName(): string { return 'createEnumColumn'; @@ -33,8 +29,6 @@ class Create extends EnumCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php index ef2c829e0a..d1418e5144 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Enum; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Enum\Update as EnumUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,7 +11,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; @@ -21,8 +19,6 @@ use Utopia\Validator\Text; class Update extends EnumUpdate { - use HTTP; - public static function getName(): string { return 'updateEnumColumn'; @@ -35,8 +31,6 @@ class Update extends EnumUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php index 32579017c6..5c99b90f52 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Float; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Float\Create as FloatCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,15 +9,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; class Create extends FloatCreate { - use HTTP; - public static function getName(): string { return 'createFloatColumn'; @@ -31,8 +27,6 @@ class Create extends FloatCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php index 4ed420cfa0..5360ca0304 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Float; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Float\Update as FloatUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,7 +10,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; @@ -19,8 +17,6 @@ use Utopia\Validator\Nullable; class Update extends FloatUpdate { - use HTTP; - public static function getName(): string { return 'updateFloatColumn'; @@ -33,8 +29,6 @@ class Update extends FloatUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php index ed28f96535..17777409d2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Get as AttributesGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,13 +9,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends AttributesGet { - use HTTP; - public static function getName(): string { return 'getColumn'; @@ -40,8 +36,6 @@ class Get extends AttributesGet public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php index 8acc47f051..cb58a14cbe 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\IP; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Create as IPCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,15 +9,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; class Create extends IPCreate { - use HTTP; - public static function getName(): string { return 'createIpColumn'; @@ -31,8 +27,6 @@ class Create extends IPCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php index c43f132c13..ef4c62a51f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\IP; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Update as IPUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,7 +10,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; @@ -19,8 +17,6 @@ use Utopia\Validator\Nullable; class Update extends IPUpdate { - use HTTP; - public static function getName(): string { return 'updateIpColumn'; @@ -33,8 +29,6 @@ class Update extends IPUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php index 7694e9d74f..6ca6cc4b93 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Integer; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Create as IntegerCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,15 +9,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; class Create extends IntegerCreate { - use HTTP; - public static function getName(): string { return 'createIntegerColumn'; @@ -31,8 +27,6 @@ class Create extends IntegerCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php index b3ce988830..764ae31f35 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Integer; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Update as IntegerUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,7 +10,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; @@ -19,8 +17,6 @@ use Utopia\Validator\Nullable; class Update extends IntegerUpdate { - use HTTP; - public static function getName(): string { return 'updateIntegerColumn'; @@ -33,8 +29,6 @@ class Update extends IntegerUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index 133f772605..20e89396a0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Relationship; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Create as RelationshipCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -11,15 +10,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\WhiteList; class Create extends RelationshipCreate { - use HTTP; - public static function getName(): string { return 'createRelationshipColumn'; @@ -32,8 +28,6 @@ class Create extends RelationshipCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/relationship') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php index ef1d8c7a33..508c5da57a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\Relationship; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Update as RelationshipUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,14 +11,11 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; class Update extends RelationshipUpdate { - use HTTP; - public static function getName(): string { return 'updateRelationshipColumn'; @@ -32,8 +28,6 @@ class Update extends RelationshipUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index ed7989fbc3..a11ee3464e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\String; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Create as StringCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,7 +9,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; use Utopia\Validator\Boolean; @@ -19,8 +17,6 @@ use Utopia\Validator\Text; class Create extends StringCreate { - use HTTP; - public static function getName(): string { return 'createStringColumn'; @@ -33,8 +29,6 @@ class Create extends StringCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php index f7ec773e3c..92df091866 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\String; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Update as StringUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,7 +10,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; use Utopia\Validator\Boolean; @@ -21,8 +19,6 @@ use Utopia\Validator\Text; class Update extends StringUpdate { - use HTTP; - public static function getName(): string { return 'updateStringColumn'; @@ -35,8 +31,6 @@ class Update extends StringUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php index 0e6fe7cff3..84c488ccfc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\URL; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\URL\Create as URLCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,15 +9,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\URL; class Create extends URLCreate { - use HTTP; - public static function getName(): string { return 'createUrlColumn'; @@ -31,8 +27,6 @@ class Create extends URLCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php index 6f3698c0cf..d3681b9358 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns\URL; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\URL\Update as URLUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,7 +10,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; @@ -19,8 +17,6 @@ use Utopia\Validator\URL; class Update extends URLUpdate { - use HTTP; - public static function getName(): string { return 'updateUrlColumn'; @@ -33,8 +29,6 @@ class Update extends URLUpdate public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index c6ceb35f48..29a108f180 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Columns; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XList as AttributesXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -10,13 +9,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Columns; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends AttributesXList { - use HTTP; - public static function getName(): string { return 'listColumns'; @@ -29,8 +25,6 @@ class XList extends AttributesXList public function __construct() { - $this->setContext(Context::DATABASE_COLUMNS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index c20865d110..273791f154 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CollectionCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,15 +11,12 @@ use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; class Create extends CollectionCreate { - use HTTP; - public static function getName(): string { return 'createTable'; @@ -33,8 +29,6 @@ class Create extends CollectionCreate public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index 85e52ac37d..0a20a20f54 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as CollectionDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,13 +9,10 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends CollectionDelete { - use HTTP; - public static function getName(): string { return 'deleteTable'; @@ -29,8 +25,6 @@ class Delete extends CollectionDelete public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index 473dfb6273..7f565231f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Get as CollectionGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,13 +9,10 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends CollectionGet { - use HTTP; - public static function getName(): string { return 'getTable'; @@ -29,8 +25,6 @@ class Get extends CollectionGet public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index e57e962e87..b11892e0f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Create as IndexCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,7 +11,6 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Integer; @@ -21,8 +19,6 @@ use Utopia\Validator\WhiteList; class Create extends IndexCreate { - use HTTP; - public static function getName(): string { return 'createColumnIndex'; @@ -35,8 +31,6 @@ class Create extends IndexCreate public function __construct() { - $this->setContext(Context::DATABASE_COLUMN_INDEX); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index 7cda1ce471..4ff9638f27 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Delete as IndexDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,13 +10,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends IndexDelete { - use HTTP; - public static function getName(): string { return 'updateColumnIndex'; @@ -34,8 +30,6 @@ class Delete extends IndexDelete public function __construct() { - $this->setContext(Context::DATABASE_COLUMN_INDEX); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index 3bee87de4a..79529fda66 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Get as IndexGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,13 +10,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Get extends IndexGet { - use HTTP; - public static function getName(): string { return 'getColumnIndex'; @@ -30,8 +26,6 @@ class Get extends IndexGet public function __construct() { - $this->setContext(Context::DATABASE_COLUMN_INDEX); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index 1af13dc82e..12581a4fc3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\XList as IndexXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,13 +10,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends IndexXList { - use HTTP; - public static function getName(): string { return 'listColumnIndexes'; @@ -30,8 +26,6 @@ class XList extends IndexXList public function __construct() { - $this->setContext(Context::DATABASE_COLUMN_INDEX); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 244cebe3ad..8e652c5416 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Logs\XList as CollectionLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,13 +11,10 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends CollectionLogXList { - use HTTP; - public static function getName(): string { return 'listTableLogs'; @@ -26,8 +22,6 @@ class XList extends CollectionLogXList public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/logs') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php index 9ddff818e3..945450aee3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Delete as DocumentsDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,15 +9,12 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; class Delete extends DocumentsDelete { - use HTTP; - public static function getName(): string { return 'deleteRows'; @@ -31,8 +27,6 @@ class Delete extends DocumentsDelete public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php index 1b9ddb60ab..916d683491 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Update as DocumentsUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; @@ -18,8 +16,6 @@ use Utopia\Validator\Text; class Update extends DocumentsUpdate { - use HTTP; - public static function getName(): string { return 'updateRows'; @@ -32,8 +28,6 @@ class Update extends DocumentsUpdate public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php index 2b4117c23e..65c848a7c4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as DocumentsUpsert; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,15 +9,12 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; class Upsert extends DocumentsUpsert { - use HTTP; - public static function getName(): string { return 'upsertRows'; @@ -31,8 +27,6 @@ class Upsert extends DocumentsUpsert public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php index 128d578cde..916c051d8a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Column; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute\Decrement as DecrementDocumentAttribute; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,14 +10,11 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Numeric; class Decrement extends DecrementDocumentAttribute { - use HTTP; - public static function getName(): string { return 'decrementRowColumn'; @@ -31,8 +27,6 @@ class Decrement extends DecrementDocumentAttribute public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/:column/decrement') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php index a3e256d561..ba78833744 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Column; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute\Increment as IncrementDocumentAttribute; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,14 +10,11 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Numeric; class Increment extends IncrementDocumentAttribute { - use HTTP; - public static function getName(): string { return 'incrementRowColumn'; @@ -31,8 +27,6 @@ class Increment extends IncrementDocumentAttribute public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/:column/increment') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index 03e5548ddb..ba2b461d6e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as DocumentCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -14,15 +13,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; class Create extends DocumentCreate { - use HTTP; - public static function getName(): string { return 'createRow'; @@ -40,8 +36,6 @@ class Create extends DocumentCreate public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php index 5edf4e0b73..27fe101f1d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DocumentDelete; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,13 +9,10 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class Delete extends DocumentDelete { - use HTTP; - public static function getName(): string { return 'deleteRow'; @@ -35,8 +31,6 @@ class Delete extends DocumentDelete public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php index 74e1f90497..8100abbea5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as DocumentGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,15 +9,12 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; class Get extends DocumentGet { - use HTTP; - public static function getName(): string { return 'getRow'; @@ -31,8 +27,6 @@ class Get extends DocumentGet public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php index 1cc5202152..f39a77323f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Logs; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Logs\XList as DocumentLogXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,13 +11,10 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; class XList extends DocumentLogXList { - use HTTP; - public static function getName(): string { return 'listRowLogs'; @@ -26,8 +22,6 @@ class XList extends DocumentLogXList public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index d37f5cc92f..9fa596ecc0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Update as DocumentUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,14 +11,11 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; class Update extends DocumentUpdate { - use HTTP; - public static function getName(): string { return 'updateRow'; @@ -32,8 +28,6 @@ class Update extends DocumentUpdate public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php index 800efa9cde..5c0fde708b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Upsert as DocumentUpsert; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -12,14 +11,11 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; class Upsert extends DocumentUpsert { - use HTTP; - public static function getName(): string { return 'upsertRow'; @@ -32,8 +28,6 @@ class Upsert extends DocumentUpsert public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php index 9d03603a0e..b7ad763e36 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\XList as DocumentXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,15 +9,12 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; class XList extends DocumentXList { - use HTTP; - public static function getName(): string { return 'listRows'; @@ -31,8 +27,6 @@ class XList extends DocumentXList public function __construct() { - $this->setContext(Context::DATABASE_ROWS); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index ab71634fb2..210b281e57 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Update as CollectionUpdate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,15 +10,12 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; class Update extends CollectionUpdate { - use HTTP; - public static function getName(): string { return 'updateTable'; @@ -32,8 +28,6 @@ class Update extends CollectionUpdate public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index 9fb5fc8c17..c82eaae7c5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Usage; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Usage\Get as CollectionUsageGet; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -10,14 +9,11 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; class Get extends CollectionUsageGet { - use HTTP; - public static function getName(): string { return 'getTableUsage'; @@ -30,8 +26,6 @@ class Get extends CollectionUsageGet public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/usage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index f6f48ace40..391b91a3dd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables; -use Appwrite\Platform\Modules\Databases\Context; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\XList as CollectionXList; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,14 +10,11 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Tables; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Text; class XList extends CollectionXList { - use HTTP; - public static function getName(): string { return 'listTables'; @@ -31,8 +27,6 @@ class XList extends CollectionXList public function __construct() { - $this->setContext(Context::DATABASE_TABLES); - $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_GET) ->setHttpPath('/v1/databases/:databaseId/tables') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php index a0e440c657..a746f4ce4c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php @@ -12,15 +12,12 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Text; class Update extends Action { - use HTTP; - public static function getName(): string { return 'updateDatabase'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php index 1a85380767..5482d25269 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/Get.php @@ -15,14 +15,11 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; class Get extends Action { - use HTTP; - public static function getName(): string { return 'getUsage'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php index 2d551465db..fd3a95cd24 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Usage/XList.php @@ -13,14 +13,11 @@ use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\WhiteList; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listUsages'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php index 7179be90ec..52f3fd2b55 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/XList.php @@ -16,14 +16,11 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Query; use Utopia\Database\Validator\Query\Cursor; use Utopia\Platform\Action; -use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Text; class XList extends Action { - use HTTP; - public static function getName(): string { return 'listDatabases'; diff --git a/src/Appwrite/Platform/Modules/Databases/Module.php b/src/Appwrite/Platform/Modules/Databases/Module.php index f8686999dd..bf336413d4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Module.php +++ b/src/Appwrite/Platform/Modules/Databases/Module.php @@ -2,6 +2,8 @@ namespace Appwrite\Platform\Modules\Databases; +require_once __DIR__ . '/Constants.php'; + use Appwrite\Platform\Modules\Databases\Services\Http; use Appwrite\Platform\Modules\Databases\Services\Workers; use Utopia\Platform; diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index a877c89345..d2f66f3657 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -14,8 +14,10 @@ class Http extends Service { $this->type = Service::TYPE_HTTP; + // Project database timeout init hook! + $this->addAction(Timeout::getName(), new Timeout()); + foreach ([ - Timeout::class, DatabasesRegistry::class, CollectionsRegistry::class, TablesRegistry::class, diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php index d036d2fd7b..bb0002ed47 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php @@ -35,6 +35,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bul use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as CreateDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DeleteDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as GetDocument; +use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Logs\XList as ListDocumentLogs; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Update as UpdateDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Upsert as UpsertDocument; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\XList as ListDocuments; @@ -90,6 +91,7 @@ class Collections extends Base $service->addAction(DeleteDocument::getName(), new DeleteDocument()); $service->addAction(DeleteDocuments::getName(), new DeleteDocuments()); $service->addAction(ListDocuments::getName(), new ListDocuments()); + $service->addAction(ListDocumentLogs::getName(), new ListDocumentLogs()); $service->addAction(IncrementDocumentAttribute::getName(), new IncrementDocumentAttribute()); $service->addAction(DecrementDocumentAttribute::getName(), new DecrementDocumentAttribute()); From 03b40262331cfae6a01b8fff5913ab3c79b9a25c Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 14:33:23 +0530 Subject: [PATCH 159/173] remove: leftover. --- .../Http/Databases/Collections/Documents/Attribute/Increment.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 90694d1c69..6755d313c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -99,7 +99,6 @@ class Increment extends Action } catch (ConflictException) { throw new Exception($this->getConflictException()); } catch (NotFoundException) { - // todo: @itznotabug what do we name this exception now? throw new Exception($this->getStructureNotFoundException()); } catch (LimitException) { throw new Exception($this->getLimitException(), $this->getSdkNamespace() . ' "' . $attribute . '" has reached the maximum value of ' . $max); From 60ab787894f963b38fb6a3449ca26e848d3d4a21 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 14:33:58 +0530 Subject: [PATCH 160/173] address comments. --- .../Databases/Http/Databases/Collections/Attributes/Action.php | 2 +- .../Databases/Http/Databases/Collections/Attributes/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Attributes/Get.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index 0a977bce32..3f331edc9a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -221,7 +221,7 @@ abstract class Action extends UtopiaAction /** * Get the proper column/attribute type based on set context. */ - final protected function getCorrectModel(string $type, string $format): string + final protected function getModel(string $type, string $format): string { $isCollections = $this->isCollectionsAPI(); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 5dba0860e2..271ea56f29 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -138,7 +138,7 @@ class Delete extends Action $type = $attribute->getAttribute('type'); $format = $attribute->getAttribute('format'); - $model = $this->getCorrectModel($type, $format); + $model = $this->getModel($type, $format); $queueForEvents ->setContext('database', $db) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index d751e8def0..579f7883e0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -91,7 +91,7 @@ class Get extends Action $attribute->setAttribute($key, $option); } - $model = $this->getCorrectModel($type, $format); + $model = $this->getModel($type, $format); $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); From 3d6acaa19c7e25bce884bbb0397d3ff4b6993a36 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 14:49:42 +0530 Subject: [PATCH 161/173] address comments. --- .../Platform/Modules/Databases/Http/Databases/Tables/Create.php | 2 +- .../Platform/Modules/Databases/Http/Databases/Tables/Update.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 273791f154..92b29ffdc6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -57,7 +57,7 @@ class Create extends CollectionCreate ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a row. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index 210b281e57..62a1b3bdee 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -54,7 +54,7 @@ class Update extends CollectionUpdate )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') - ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') + ->param('name', null, new Text(128), 'Table name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) From caf23083003a4ac44897018a684ffe38ff6328a0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 14:50:15 +0530 Subject: [PATCH 162/173] address comment. --- .../Modules/Databases/Http/Databases/Collections/XList.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index ae63a40e64..3a9868dec2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -65,11 +65,6 @@ class XList extends Action public function action(string $databaseId, array $queries, string $search, UtopiaResponse $response, Database $dbForProject): void { - print_r(json_encode([ - 'context' => $this->getContext(), - 'model' => $this->getResponseModel() - ], JSON_PRETTY_PRINT)); - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { From 5287a3d6190960320888ab2ebe7bb88779927f41 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 14:52:01 +0530 Subject: [PATCH 163/173] address comment. --- .../Databases/Http/Databases/Collections/Attributes/Delete.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 271ea56f29..5816de31a2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -26,7 +26,6 @@ class Delete extends Action protected function getResponseModel(): string|array { - // we should correctly & carefully set the context later. return UtopiaResponse::MODEL_NONE; } From 7ebe8f6c9e687b665166d481697c60866a9addc6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 17:34:16 +0530 Subject: [PATCH 164/173] revert: legacy tests namespacing to old. --- tests/e2e/Services/GraphQL/Base.php | 80 ++++++------ .../{Collections => Legacy}/AbuseTest.php | 2 +- .../{Collections => Legacy}/AuthTest.php | 8 +- .../DatabaseClientTest.php | 22 ++-- .../DatabaseServerTest.php | 118 +++++++++--------- 5 files changed, 115 insertions(+), 115 deletions(-) rename tests/e2e/Services/GraphQL/{Collections => Legacy}/AbuseTest.php (99%) rename tests/e2e/Services/GraphQL/{Collections => Legacy}/AuthTest.php (96%) rename tests/e2e/Services/GraphQL/{Collections => Legacy}/DatabaseClientTest.php (94%) rename tests/e2e/Services/GraphQL/{Collections => Legacy}/DatabaseServerTest.php (91%) diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index f001265caf..3da4dc11a3 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -626,7 +626,7 @@ trait Base }'; case self::$CREATE_STRING_ATTRIBUTE: return 'mutation createStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $size: Int!, $required: Boolean!, $default: String, $array: Boolean){ - collectionsCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { + databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, size: $size, required: $required, default: $default, array: $array) { key required default @@ -635,7 +635,7 @@ trait Base }'; case self::$CREATE_INTEGER_ATTRIBUTE: return 'mutation createIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int, $max: Int, $default: Int, $array: Boolean){ - collectionsCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -646,7 +646,7 @@ trait Base }'; case self::$CREATE_FLOAT_ATTRIBUTE: return 'mutation createFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float, $max: Float, $default: Float, $array: Boolean){ - collectionsCreateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { + databasesCreateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default, array: $array) { key required min @@ -657,7 +657,7 @@ trait Base }'; case self::$CREATE_BOOLEAN_ATTRIBUTE: return 'mutation createBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean, $array: Boolean){ - collectionsCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -666,7 +666,7 @@ trait Base }'; case self::$CREATE_URL_ATTRIBUTE: return 'mutation createUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - collectionsCreateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + databasesCreateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -675,7 +675,7 @@ trait Base }'; case self::$CREATE_EMAIL_ATTRIBUTE: return 'mutation createEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - collectionsCreateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + databasesCreateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -684,7 +684,7 @@ trait Base }'; case self::$CREATE_IP_ATTRIBUTE: return 'mutation createIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - collectionsCreateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + databasesCreateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -693,7 +693,7 @@ trait Base }'; case self::$CREATE_ENUM_ATTRIBUTE: return 'mutation createEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String, $array: Boolean){ - collectionsCreateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { + databasesCreateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default, array: $array) { key elements required @@ -703,7 +703,7 @@ trait Base }'; case self::$CREATE_DATETIME_ATTRIBUTE: return 'mutation createDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String, $array: Boolean){ - collectionsCreateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { + databasesCreateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default, array: $array) { key required default @@ -712,7 +712,7 @@ trait Base }'; case self::$CREATE_RELATIONSHIP_ATTRIBUTE: return 'mutation createRelationshipAttribute($databaseId: String!, $collectionId: String!, $relatedCollectionId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){ - collectionsCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { + databasesCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) { relatedCollection relationType twoWay @@ -723,14 +723,14 @@ trait Base }'; case self::$UPDATE_STRING_ATTRIBUTE: return 'mutation updateStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - collectionsUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + databasesUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_INTEGER_ATTRIBUTE: return 'mutation updateIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){ - collectionsUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) { + databasesUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) { required min max @@ -739,7 +739,7 @@ trait Base }'; case self::$UPDATE_FLOAT_ATTRIBUTE: return 'mutation updateFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){ - collectionsUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) { + databasesUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) { required min max @@ -748,35 +748,35 @@ trait Base }'; case self::$UPDATE_BOOLEAN_ATTRIBUTE: return 'mutation updateBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean){ - collectionsUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + databasesUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_URL_ATTRIBUTE: return 'mutation updateUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - collectionsUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + databasesUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_EMAIL_ATTRIBUTE: return 'mutation updateEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - collectionsUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + databasesUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_IP_ATTRIBUTE: return 'mutation updateIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - collectionsUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + databasesUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_ENUM_ATTRIBUTE: return 'mutation updateEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){ - collectionsUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) { + databasesUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) { elements required default @@ -784,14 +784,14 @@ trait Base }'; case self::$UPDATE_DATETIME_ATTRIBUTE: return 'mutation updateDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){ - collectionsUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { + databasesUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) { required default } }'; case self::$UPDATE_RELATIONSHIP_ATTRIBUTE: return 'mutation updateRelationshipAttribute($databaseId: String!, $collectionId: String!, $key: String!, $onDelete: String){ - collectionsUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) { + databasesUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) { relatedCollection relationType twoWay @@ -978,7 +978,7 @@ trait Base }'; case self::$CREATE_INDEX: return 'mutation createIndex($databaseId: String!, $collectionId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){ - collectionsCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { + databasesCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) { key type status @@ -986,7 +986,7 @@ trait Base }'; case self::$GET_INDEXES: return 'query listIndexes($databaseId: String!, $collectionId: String!) { - collectionsListIndexes(databaseId: $databaseId, collectionId: $collectionId) { + databasesListIndexes(databaseId: $databaseId, collectionId: $collectionId) { total indexes { key @@ -997,7 +997,7 @@ trait Base }'; case self::$GET_INDEX: return 'query getIndex($databaseId: String!, $collectionId: String!, $key: String!) { - collectionsGetIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + databasesGetIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { key type status @@ -1005,7 +1005,7 @@ trait Base }'; case self::$DELETE_INDEX: return 'mutation deleteIndex($databaseId: String!, $collectionId: String!, $key: String!) { - collectionsDeleteIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + databasesDeleteIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key) { status } }'; @@ -1044,7 +1044,7 @@ trait Base }'; case self::$GET_ATTRIBUTES: return 'query listAttributes($databaseId: String!, $collectionId: String!) { - collectionsListAttributes(databaseId: $databaseId, collectionId: $collectionId) { + databasesListAttributes(databaseId: $databaseId, collectionId: $collectionId) { total attributes { ...attributeProperties @@ -1053,13 +1053,13 @@ trait Base }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; case self::$GET_ATTRIBUTE: return 'query getAttribute($databaseId: String!, $collectionId: String!, $key: String!) { - collectionsGetAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + databasesGetAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { ...attributeProperties } }' . PHP_EOL . self::$FRAGMENT_ATTRIBUTES; case self::$DELETE_ATTRIBUTE: return 'mutation deleteAttribute($databaseId: String!, $collectionId: String!, $key: String!) { - collectionsDeleteAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { + databasesDeleteAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key) { status } }'; @@ -1087,7 +1087,7 @@ trait Base case self::$GET_DOCUMENT: return 'query getDocument($databaseId: String!, $collectionId: String!, $documentId: String!) { - collectionsGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { + databasesGetDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { _id _collectionId _permissions @@ -1096,7 +1096,7 @@ trait Base }'; case self::$GET_DOCUMENTS: return 'query listDocuments($databaseId: String!, $collectionId: String!){ - collectionsListDocuments(databaseId: $databaseId, collectionId: $collectionId) { + databasesListDocuments(databaseId: $databaseId, collectionId: $collectionId) { total documents { _id @@ -1108,7 +1108,7 @@ trait Base }'; case self::$CREATE_DOCUMENT: return 'mutation createDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ - collectionsCreateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + databasesCreateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id _collectionId _permissions @@ -1116,7 +1116,7 @@ trait Base }'; case self::$CREATE_DOCUMENTS: return 'mutation createDocuments($databaseId: String!, $collectionId: String!, $documents: [Json!]!) { - collectionsCreateDocuments(databaseId: $databaseId, collectionId: $collectionId, documents: $documents) { + databasesCreateDocuments(databaseId: $databaseId, collectionId: $collectionId, documents: $documents) { documents { _id _collectionId @@ -1227,7 +1227,7 @@ trait Base }'; case self::$UPDATE_DOCUMENT: return 'mutation updateDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!]){ - collectionsUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + databasesUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id _collectionId data @@ -1235,7 +1235,7 @@ trait Base }'; case self::$UPSERT_DOCUMENT: return 'mutation upsertDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $permissions: [String!] = []) { - collectionsUpsertDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { + databasesUpsertDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, permissions: $permissions) { _id _databaseId _collectionId @@ -1244,7 +1244,7 @@ trait Base }'; case self::$UPDATE_DOCUMENTS: return 'mutation updateDocuments($databaseId: String!, $collectionId: String!, $data: Json!, $queries: [String!]) { - collectionsUpdateDocuments(databaseId: $databaseId, collectionId: $collectionId, data: $data, queries: $queries) { + databasesUpdateDocuments(databaseId: $databaseId, collectionId: $collectionId, data: $data, queries: $queries) { total documents { _id @@ -1257,7 +1257,7 @@ trait Base }'; case self::$UPSERT_DOCUMENTS: return 'mutation upsertDocuments($databaseId: String!, $collectionId: String!, $documents: [Json!]!) { - collectionsUpsertDocuments(databaseId: $databaseId, collectionId: $collectionId, documents: $documents) { + databasesUpsertDocuments(databaseId: $databaseId, collectionId: $collectionId, documents: $documents) { total documents { _id @@ -1270,7 +1270,7 @@ trait Base }'; case self::$DELETE_DOCUMENTS: return 'mutation deleteDocuments($databaseId: String!, $collectionId: String!, $queries: [String!] = []) { - collectionsDeleteDocuments(databaseId: $databaseId, collectionId: $collectionId, queries: $queries) { + databasesDeleteDocuments(databaseId: $databaseId, collectionId: $collectionId, queries: $queries) { total documents { _id @@ -1282,7 +1282,7 @@ trait Base }'; case self::$DELETE_DOCUMENT: return 'mutation deleteDocument($databaseId: String!, $collectionId: String!, $documentId: String!){ - collectionsDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { + databasesDeleteDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId) { status } }'; @@ -2844,7 +2844,7 @@ trait Base status } } - collectionsCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "name", size: 255, required: true) { + databasesCreateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "name", size: 255, required: true) { key type status @@ -2853,7 +2853,7 @@ trait Base default array } - collectionsCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "age", min: 0, max: 150, required: true) { + databasesCreateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "age", min: 0, max: 150, required: true) { key type status @@ -2863,7 +2863,7 @@ trait Base default array } - collectionsCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "alive", required: false, default: true) { + databasesCreateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: "alive", required: false, default: true) { key type status diff --git a/tests/e2e/Services/GraphQL/Collections/AbuseTest.php b/tests/e2e/Services/GraphQL/Legacy/AbuseTest.php similarity index 99% rename from tests/e2e/Services/GraphQL/Collections/AbuseTest.php rename to tests/e2e/Services/GraphQL/Legacy/AbuseTest.php index a58a57ec5a..d579ddb128 100644 --- a/tests/e2e/Services/GraphQL/Collections/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/AbuseTest.php @@ -1,6 +1,6 @@ [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], - 'documentId' => $document['body']['data']['collectionsCreateDocument']['_id'], + 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], ] ]; $document = $this->client->call(Client::METHOD_POST, '/graphql', [ @@ -188,7 +188,7 @@ class AuthTest extends Scope 'cookie' => 'a_session_' . $projectId . '=' . $this->token1, ], $gqlPayload); - $this->assertIsArray($document['body']['data']['collectionsGetDocument']); + $this->assertIsArray($document['body']['data']['databasesGetDocument']); $this->assertArrayNotHasKey('errors', $document['body']); // Try to read as account 2 @@ -238,7 +238,7 @@ class AuthTest extends Scope 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], 'collectionId' => $this->collection['body']['data']['databasesCreateCollection']['_id'], - 'documentId' => $document['body']['data']['collectionsCreateDocument']['_id'], + 'documentId' => $document['body']['data']['databasesCreateDocument']['_id'], ] ]; $document = $this->client->call(Client::METHOD_POST, '/graphql', [ diff --git a/tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php similarity index 94% rename from tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php rename to tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php index 349d580d64..c2c63e41c0 100644 --- a/tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php @@ -1,6 +1,6 @@ assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateStringAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); return $data; } @@ -141,7 +141,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateIntegerAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); return $data; } @@ -182,7 +182,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['collectionsCreateDocument']; + $document = $document['body']['data']['databasesCreateDocument']; $this->assertIsArray($document); return [ @@ -215,7 +215,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['collectionsListDocuments']); + $this->assertIsArray($documents['body']['data']['databasesListDocuments']); } /** @@ -242,7 +242,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['collectionsGetDocument']); + $this->assertIsArray($document['body']['data']['databasesGetDocument']); } /** @@ -272,7 +272,7 @@ class DatabaseClientTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['collectionsUpdateDocument']; + $document = $document['body']['data']['databasesUpdateDocument']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); @@ -377,7 +377,7 @@ class DatabaseClientTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(10, $res['body']['data']['collectionsCreateDocuments']['documents']); + $this->assertCount(10, $res['body']['data']['databasesCreateDocuments']['documents']); return [ 'databaseId' => $databaseId, @@ -418,7 +418,7 @@ class DatabaseClientTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(10, $res['body']['data']['collectionsUpdateDocuments']['documents']); + $this->assertCount(10, $res['body']['data']['databasesUpdateDocuments']['documents']); return $data; } @@ -449,7 +449,7 @@ class DatabaseClientTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(2, $res['body']['data']['collectionsUpsertDocuments']['documents']); + $this->assertCount(2, $res['body']['data']['databasesUpsertDocuments']['documents']); return $data; } @@ -475,7 +475,7 @@ class DatabaseClientTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(11, $res['body']['data']['collectionsDeleteDocuments']['documents']); + $this->assertCount(11, $res['body']['data']['databasesDeleteDocuments']['documents']); return $data; } diff --git a/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php similarity index 91% rename from tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php rename to tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php index 3e5b457308..3ecc96eaa0 100644 --- a/tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php @@ -1,6 +1,6 @@ assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateStringAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateStringAttribute']); return $data; } @@ -170,9 +170,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateStringAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateStringAttribute']['required']); - $this->assertEquals('Default Value', $attribute['body']['data']['collectionsUpdateStringAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateStringAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateStringAttribute']['required']); + $this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -205,7 +205,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateIntegerAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIntegerAttribute']); return $data; } @@ -240,11 +240,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateIntegerAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateIntegerAttribute']['required']); - $this->assertEquals(12, $attribute['body']['data']['collectionsUpdateIntegerAttribute']['min']); - $this->assertEquals(160, $attribute['body']['data']['collectionsUpdateIntegerAttribute']['max']); - $this->assertEquals(50, $attribute['body']['data']['collectionsUpdateIntegerAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateIntegerAttribute']['required']); + $this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerAttribute']['min']); + $this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerAttribute']['max']); + $this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -275,7 +275,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateBooleanAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateBooleanAttribute']); return $data; } @@ -308,9 +308,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateBooleanAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateBooleanAttribute']['required']); - $this->assertTrue($attribute['body']['data']['collectionsUpdateBooleanAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateBooleanAttribute']['required']); + $this->assertTrue($attribute['body']['data']['databasesUpdateBooleanAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -344,7 +344,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateFloatAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateFloatAttribute']); return $data; } @@ -379,11 +379,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateFloatAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateFloatAttribute']['required']); - $this->assertEquals(100.0, $attribute['body']['data']['collectionsUpdateFloatAttribute']['min']); - $this->assertEquals(1000000.0, $attribute['body']['data']['collectionsUpdateFloatAttribute']['max']); - $this->assertEquals(2500.0, $attribute['body']['data']['collectionsUpdateFloatAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateFloatAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateFloatAttribute']['required']); + $this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['min']); + $this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['max']); + $this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -414,7 +414,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateEmailAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateEmailAttribute']); return $data; } @@ -447,9 +447,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateEmailAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateEmailAttribute']['required']); - $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['collectionsUpdateEmailAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateEmailAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateEmailAttribute']['required']); + $this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -485,7 +485,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateEnumAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateEnumAttribute']); return $data; } @@ -524,11 +524,11 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateEnumAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateEnumAttribute']['required']); - $this->assertEquals('tech', $attribute['body']['data']['collectionsUpdateEnumAttribute']['default']); - $this->assertContains('tech', $attribute['body']['data']['collectionsUpdateEnumAttribute']['elements']); - $this->assertNotContains('guest', $attribute['body']['data']['collectionsUpdateEnumAttribute']['elements']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateEnumAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateEnumAttribute']['required']); + $this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['default']); + $this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); + $this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -559,7 +559,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateDatetimeAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateDatetimeAttribute']); return $data; } @@ -592,9 +592,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateDatetimeAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateDatetimeAttribute']['required']); - $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['collectionsUpdateDatetimeAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeAttribute']['required']); + $this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -627,7 +627,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateRelationshipAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipAttribute']); return $data; } @@ -658,7 +658,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateRelationshipAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipAttribute']); return $data; } @@ -689,7 +689,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateIpAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateIpAttribute']); return $data; } @@ -722,9 +722,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateIpAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateIpAttribute']['required']); - $this->assertEquals('127.0.0.1', $attribute['body']['data']['collectionsUpdateIpAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateIpAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateIpAttribute']['required']); + $this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); return $data; @@ -756,7 +756,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsCreateUrlAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesCreateUrlAttribute']); return $data; } @@ -789,9 +789,9 @@ class DatabaseServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsUpdateUrlAttribute']); - $this->assertFalse($attribute['body']['data']['collectionsUpdateUrlAttribute']['required']); - $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['collectionsUpdateUrlAttribute']['default']); + $this->assertIsArray($attribute['body']['data']['databasesUpdateUrlAttribute']); + $this->assertFalse($attribute['body']['data']['databasesUpdateUrlAttribute']['required']); + $this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlAttribute']['default']); $this->assertEquals(200, $attribute['headers']['status-code']); } @@ -825,12 +825,12 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $index['body']); $this->assertIsArray($index['body']['data']); - $this->assertIsArray($index['body']['data']['collectionsCreateIndex']); + $this->assertIsArray($index['body']['data']['databasesCreateIndex']); return [ 'database' => $data['database'], 'collection' => $data['collection'], - 'index' => $index['body']['data']['collectionsCreateIndex'], + 'index' => $index['body']['data']['databasesCreateIndex'], ]; } @@ -876,7 +876,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['collectionsCreateDocument']; + $document = $document['body']['data']['databasesCreateDocument']; $this->assertIsArray($document); return [ @@ -1044,7 +1044,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attributes['body']); $this->assertIsArray($attributes['body']['data']); - $this->assertIsArray($attributes['body']['data']['collectionsListAttributes']); + $this->assertIsArray($attributes['body']['data']['databasesListAttributes']); } /** @@ -1071,7 +1071,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $attribute['body']); $this->assertIsArray($attribute['body']['data']); - $this->assertIsArray($attribute['body']['data']['collectionsGetAttribute']); + $this->assertIsArray($attribute['body']['data']['databasesGetAttribute']); } /** @@ -1097,7 +1097,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $indices['body']); $this->assertIsArray($indices['body']['data']); - $this->assertIsArray($indices['body']['data']['collectionsListIndexes']); + $this->assertIsArray($indices['body']['data']['databasesListIndexes']); } /** @@ -1124,7 +1124,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $index['body']); $this->assertIsArray($index['body']['data']); - $this->assertIsArray($index['body']['data']['collectionsGetIndex']); + $this->assertIsArray($index['body']['data']['databasesGetIndex']); } /** @@ -1150,7 +1150,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $documents['body']); $this->assertIsArray($documents['body']['data']); - $this->assertIsArray($documents['body']['data']['collectionsListDocuments']); + $this->assertIsArray($documents['body']['data']['databasesListDocuments']); } /** @@ -1177,7 +1177,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $this->assertIsArray($document['body']['data']['collectionsGetDocument']); + $this->assertIsArray($document['body']['data']['databasesGetDocument']); } // /** @@ -1308,7 +1308,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $document['body']); $this->assertIsArray($document['body']['data']); - $document = $document['body']['data']['collectionsUpdateDocument']; + $document = $document['body']['data']['databasesUpdateDocument']; $this->assertIsArray($document); $this->assertStringContainsString('New Document Name', $document['data']); } @@ -1538,7 +1538,7 @@ class DatabaseServerTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(10, $res['body']['data']['collectionsCreateDocuments']['documents']); + $this->assertCount(10, $res['body']['data']['databasesCreateDocuments']['documents']); return [ 'databaseId' => $databaseId, @@ -1578,7 +1578,7 @@ class DatabaseServerTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(10, $res['body']['data']['collectionsUpdateDocuments']['documents']); + $this->assertCount(10, $res['body']['data']['databasesUpdateDocuments']['documents']); return $data; } @@ -1608,7 +1608,7 @@ class DatabaseServerTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(2, $res['body']['data']['collectionsUpsertDocuments']['documents']); + $this->assertCount(2, $res['body']['data']['databasesUpsertDocuments']['documents']); return $data; } @@ -1633,7 +1633,7 @@ class DatabaseServerTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $this->assertCount(11, $res['body']['data']['collectionsDeleteDocuments']['documents']); + $this->assertCount(11, $res['body']['data']['databasesDeleteDocuments']['documents']); return $data; } From 531f50a2c74b279a98ab85b9aaf61f53f0f81e1d Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 17:59:29 +0530 Subject: [PATCH 165/173] fix: sdk namespacing and graphql tests. --- .../Http/Databases/Collections/Action.php | 8 ++++++++ .../Http/Databases/Tables/Columns/XList.php | 2 +- .../Http/Databases/Tables/Create.php | 6 +++--- .../Http/Databases/Tables/Delete.php | 6 +++--- .../Databases/Http/Databases/Tables/Get.php | 6 +++--- .../Http/Databases/Tables/Update.php | 6 +++--- .../Http/Databases/Tables/Usage/Get.php | 2 +- .../Databases/Http/Databases/Tables/XList.php | 8 ++++---- .../Database/Validator/Queries/Attributes.php | 2 +- .../Validator/Queries/Collections.php | 2 +- .../Database/Validator/Queries/Columns.php | 4 ++-- .../Database/Validator/Queries/Tables.php | 6 +++--- tests/e2e/Services/GraphQL/Base.php | 20 +++++++++---------- .../e2e/Services/GraphQL/Tables/AbuseTest.php | 4 ++-- .../e2e/Services/GraphQL/Tables/AuthTest.php | 10 +++++----- .../GraphQL/Tables/DatabaseClientTest.php | 4 ++-- .../GraphQL/Tables/DatabaseServerTest.php | 12 +++++------ 17 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php index 0e3202d979..9329300ffa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php @@ -59,6 +59,14 @@ abstract class Action extends UtopiaAction return $this->isCollectionsAPI() ? 'collections' : 'tables'; } + /** + * Get the SDK namespace for the current action. + */ + final protected function getSdkNamespace(): string + { + return $this->isCollectionsAPI() ? 'databases' : 'tables'; + } + /** * Get the exception to throw when the resource already exists. */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index 29a108f180..8190f2ec71 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -47,7 +47,7 @@ class XList extends AttributesXList )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') - ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Columns::ALLOWED_ATTRIBUTES), true) + ->param('queries', [], new Columns(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Columns::ALLOWED_COLUMNS), true) ->inject('response') ->inject('dbForProject') ->callback($this->action(...)); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 92b29ffdc6..0bfdef5249 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -19,7 +19,7 @@ class Create extends CollectionCreate { public static function getName(): string { - return 'createTable'; + return 'create'; } protected function getResponseModel(): string @@ -40,8 +40,8 @@ class Create extends CollectionCreate ->label('audits.event', 'table.create') ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') ->label('sdk', new Method( - namespace: 'databases', - group: $this->getSdkGroup(), + namespace: $this->getSdkNamespace(), + group: null, name: self::getName(), description: '/docs/references/databases/create-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index 0a20a20f54..caa7197acb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -15,7 +15,7 @@ class Delete extends CollectionDelete { public static function getName(): string { - return 'deleteTable'; + return 'delete'; } protected function getResponseModel(): string @@ -36,8 +36,8 @@ class Delete extends CollectionDelete ->label('audits.event', 'table.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', - group: $this->getSdkGroup(), + namespace: $this->getSdkNamespace(), + group: null, name: self::getName(), description: '/docs/references/databases/delete-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index 7f565231f9..40672ba51f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -15,7 +15,7 @@ class Get extends CollectionGet { public static function getName(): string { - return 'getTable'; + return 'get'; } protected function getResponseModel(): string @@ -33,8 +33,8 @@ class Get extends CollectionGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', - group: $this->getSdkGroup(), + namespace: $this->getSdkNamespace(), + group: null, name: self::getName(), description: '/docs/references/databases/get-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index 62a1b3bdee..f800e7b1de 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -18,7 +18,7 @@ class Update extends CollectionUpdate { public static function getName(): string { - return 'updateTable'; + return 'update'; } protected function getResponseModel(): string @@ -39,8 +39,8 @@ class Update extends CollectionUpdate ->label('audits.event', 'table.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: 'databases', - group: $this->getSdkGroup(), + namespace: $this->getSdkNamespace(), + group: null, name: self::getName(), description: '/docs/references/databases/update-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index c82eaae7c5..ca990eeab6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -34,7 +34,7 @@ class Get extends CollectionUsageGet ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: null, name: self::getName(), description: '/docs/references/databases/get-collection-usage.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index 391b91a3dd..34000d97b1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -17,7 +17,7 @@ class XList extends CollectionXList { public static function getName(): string { - return 'listTables'; + return 'list'; } protected function getResponseModel(): string @@ -35,8 +35,8 @@ class XList extends CollectionXList ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', - group: $this->getSdkGroup(), + namespace: $this->getSdkNamespace(), + group: null, name: self::getName(), description: '/docs/references/databases/list-collections.md', auth: [AuthType::KEY], @@ -49,7 +49,7 @@ class XList extends CollectionXList contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('queries', [], new Tables(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Tables::ALLOWED_ATTRIBUTES), true) + ->param('queries', [], new Tables(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Tables::ALLOWED_COLUMNS), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php b/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php index 4a35c82b73..953948c045 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Attributes.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class Attributes extends Base { - public const ALLOWED_ATTRIBUTES = [ + public const array ALLOWED_ATTRIBUTES = [ 'key', 'type', 'size', diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Collections.php b/src/Appwrite/Utopia/Database/Validator/Queries/Collections.php index 6e8fcb8339..cdaaa104dc 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Collections.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Collections.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class Collections extends Base { - public const ALLOWED_ATTRIBUTES = [ + public const array ALLOWED_ATTRIBUTES = [ 'name', 'enabled', 'documentSecurity' diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Columns.php b/src/Appwrite/Utopia/Database/Validator/Queries/Columns.php index 3c2742bfdd..c990dfc18a 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Columns.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Columns.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class Columns extends Base { - public const ALLOWED_ATTRIBUTES = [ + public const array ALLOWED_COLUMNS = [ 'key', 'type', 'size', @@ -20,6 +20,6 @@ class Columns extends Base */ public function __construct() { - parent::__construct('attributes', self::ALLOWED_ATTRIBUTES); + parent::__construct('attributes', self::ALLOWED_COLUMNS); } } diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Tables.php b/src/Appwrite/Utopia/Database/Validator/Queries/Tables.php index 5a1fc4ec6d..f3aadb69ac 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Tables.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Tables.php @@ -4,10 +4,10 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class Tables extends Base { - public const ALLOWED_ATTRIBUTES = [ + public const array ALLOWED_COLUMNS = [ 'name', 'enabled', - 'documentSecurity' + 'rowSecurity' ]; /** @@ -16,6 +16,6 @@ class Tables extends Base */ public function __construct() { - parent::__construct('collections', self::ALLOWED_ATTRIBUTES); + parent::__construct('collections', self::ALLOWED_COLUMNS); } } diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 3da4dc11a3..bd3c792248 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -580,8 +580,8 @@ trait Base } }'; case self::$GET_TABLE: - return 'query getTable($databaseId: String!, $tableId: String!) { - databasesGetTable(databaseId: $databaseId, tableId: $tableId) { + return 'query tablesGet($databaseId: String!, $tableId: String!) { + tablesGet(databaseId: $databaseId, tableId: $tableId) { _id _permissions rowSecurity @@ -589,8 +589,8 @@ trait Base } }'; case self::$GET_TABLES: - return 'query listTables($databaseId: String!) { - databasesListTables(databaseId: $databaseId) { + return 'query tablesList($databaseId: String!) { + tablesList(databaseId: $databaseId) { total tables { _id @@ -601,8 +601,8 @@ trait Base } }'; case self::$CREATE_TABLE: - return 'mutation createTable($databaseId: String!, $tableId: String!, $name: String!, $rowSecurity: Boolean!, $permissions: [String!]!) { - databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $name, rowSecurity: $rowSecurity, permissions: $permissions) { + return 'mutation tablesCreate($databaseId: String!, $tableId: String!, $name: String!, $rowSecurity: Boolean!, $permissions: [String!]!) { + tablesCreate(databaseId: $databaseId, tableId: $tableId, name: $name, rowSecurity: $rowSecurity, permissions: $permissions) { _id _permissions rowSecurity @@ -610,8 +610,8 @@ trait Base } }'; case self::$UPDATE_TABLE: - return 'mutation updateTable($databaseId: String!, $tableId: String!, $name: String!, $rowSecurity: Boolean!, $permissions: [String!], $enabled: Boolean) { - databasesUpdateTable(databaseId: $databaseId, tableId: $tableId, name: $name, rowSecurity: $rowSecurity, permissions: $permissions, enabled: $enabled) { + return 'mutation tablesUpdate($databaseId: String!, $tableId: String!, $name: String!, $rowSecurity: Boolean!, $permissions: [String!], $enabled: Boolean) { + tablesUpdate(databaseId: $databaseId, tableId: $tableId, name: $name, rowSecurity: $rowSecurity, permissions: $permissions, enabled: $enabled) { _id _permissions rowSecurity @@ -619,8 +619,8 @@ trait Base } }'; case self::$DELETE_TABLE: - return 'mutation deleteTable($databaseId: String!, $tableId: String!) { - databasesDeleteTable(databaseId: $databaseId, tableId: $tableId) { + return 'mutation tablesDelete($databaseId: String!, $tableId: String!) { + tablesDelete(databaseId: $databaseId, tableId: $tableId) { status } }'; diff --git a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php index abb5019d55..8b759aa1ce 100644 --- a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php @@ -132,7 +132,7 @@ class AbuseTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - $databaseId = $response['body']['data']['databasesCreate']['_id']; + $databaseId = $response['body']['data']['tablesCreate']['_id']; $query = $this->getQuery(self::$CREATE_TABLE); $gqlPayload = [ @@ -155,7 +155,7 @@ class AbuseTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - $tableId = $response['body']['data']['databasesCreateTable']['_id']; + $tableId = $response['body']['data']['tablesCreate']['_id']; $query = $this->getQuery(self::$CREATE_STRING_COLUMN); $gqlPayload = [ diff --git a/tests/e2e/Services/GraphQL/Tables/AuthTest.php b/tests/e2e/Services/GraphQL/Tables/AuthTest.php index 681607d599..21d581d207 100644 --- a/tests/e2e/Services/GraphQL/Tables/AuthTest.php +++ b/tests/e2e/Services/GraphQL/Tables/AuthTest.php @@ -128,7 +128,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'tableId' => $this->table['body']['data']['tablesCreate']['_id'], 'key' => 'name', 'size' => 256, 'required' => true, @@ -154,7 +154,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'tableId' => $this->table['body']['data']['tablesCreate']['_id'], 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', @@ -178,7 +178,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'tableId' => $this->table['body']['data']['tablesCreate']['_id'], 'rowId' => $row['body']['data']['tablesCreateRow']['_id'], ] ]; @@ -213,7 +213,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'tableId' => $this->table['body']['data']['tablesCreate']['_id'], 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', @@ -237,7 +237,7 @@ class AuthTest extends Scope 'query' => $query, 'variables' => [ 'databaseId' => $this->database['body']['data']['databasesCreate']['_id'], - 'tableId' => $this->table['body']['data']['databasesCreateTable']['_id'], + 'tableId' => $this->table['body']['data']['tablesCreate']['_id'], 'rowId' => $row['body']['data']['tablesCreateRow']['_id'], ] ]; diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php index c5cba6c47f..5ff8e65922 100644 --- a/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php @@ -75,7 +75,7 @@ class DatabaseClientTest extends Scope $this->assertIsArray($table['body']['data']); $this->assertArrayNotHasKey('errors', $table['body']); - $table = $table['body']['data']['databasesCreateTable']; + $table = $table['body']['data']['tablesCreate']; $this->assertEquals('Actors', $table['name']); return [ @@ -349,7 +349,7 @@ class DatabaseClientTest extends Scope $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $tableId = $res['body']['data']['databasesCreateTable']['_id']; + $tableId = $res['body']['data']['tablesCreate']['_id']; // Step 3: Create column $query = $this->getQuery(self::$CREATE_STRING_COLUMN); diff --git a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php index 61cb57911a..e4e9b34f35 100644 --- a/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php @@ -75,7 +75,7 @@ class DatabaseServerTest extends Scope $this->assertIsArray($table['body']['data']); $this->assertArrayNotHasKey('errors', $table['body']); - $table = $table['body']['data']['databasesCreateTable']; + $table = $table['body']['data']['tablesCreate']; $this->assertEquals('Actors', $table['name']); $gqlPayload = [ @@ -101,7 +101,7 @@ class DatabaseServerTest extends Scope $this->assertIsArray($table2['body']['data']); $this->assertArrayNotHasKey('errors', $table2['body']); - $table2 = $table2['body']['data']['databasesCreateTable']; + $table2 = $table2['body']['data']['tablesCreate']; $this->assertEquals('Movies', $table2['name']); return [ @@ -992,7 +992,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $tables['body']); $this->assertIsArray($tables['body']['data']); - $this->assertIsArray($tables['body']['data']['databasesListTables']); + $this->assertIsArray($tables['body']['data']['tablesList']); } /** @@ -1018,7 +1018,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $table['body']); $this->assertIsArray($table['body']['data']); - $this->assertIsArray($table['body']['data']['databasesGetTable']); + $this->assertIsArray($table['body']['data']['tablesGet']); } /** @@ -1279,7 +1279,7 @@ class DatabaseServerTest extends Scope $this->assertArrayNotHasKey('errors', $table['body']); $this->assertIsArray($table['body']['data']); - $this->assertIsArray($table['body']['data']['databasesUpdateTable']); + $this->assertIsArray($table['body']['data']['tablesUpdate']); } /** @@ -1510,7 +1510,7 @@ class DatabaseServerTest extends Scope $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - $tableId = $res['body']['data']['databasesCreateTable']['_id']; + $tableId = $res['body']['data']['tablesCreate']['_id']; // Step 3: Create column $query = $this->getQuery(self::$CREATE_STRING_COLUMN); From 4521302cb87edb7419a1585b2b0a389bb8e8dc66 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 19 Jun 2025 18:36:32 +0530 Subject: [PATCH 166/173] fix: graphql test for complex queries. --- tests/e2e/Services/GraphQL/Base.php | 2 +- tests/e2e/Services/GraphQL/Tables/AbuseTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index bd3c792248..554f84e616 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -3070,7 +3070,7 @@ trait Base _id name } - databasesCreateTable(databaseId: $databaseId, tableId: $tableId, name: $tableName, rowSecurity: $rowSecurity, permissions: $tablePermissions) { + tablesCreate(databaseId: $databaseId, tableId: $tableId, name: $tableName, rowSecurity: $rowSecurity, permissions: $tablePermissions) { _id _createdAt _updatedAt diff --git a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php index 8b759aa1ce..88c216dd5a 100644 --- a/tests/e2e/Services/GraphQL/Tables/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Tables/AbuseTest.php @@ -132,7 +132,7 @@ class AbuseTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - $databaseId = $response['body']['data']['tablesCreate']['_id']; + $databaseId = $response['body']['data']['databasesCreate']['_id']; $query = $this->getQuery(self::$CREATE_TABLE); $gqlPayload = [ From b52c49389c6cfc820b977d9b3b05ca926a172170 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 23 Jun 2025 12:28:14 +0530 Subject: [PATCH 167/173] fix: oha as per latest updates. --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6d73787d00..6b159985f2 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -65,7 +65,7 @@ jobs: sudo apt update sudo apt install oha - name: Benchmark PR - run: 'oha -z 180s http://localhost/v1/health/version -j > benchmark.json' + run: 'oha -z 180s http://localhost/v1/health/version -o json > benchmark.json' - name: Cleaning run: docker compose down -v - name: Installing latest version @@ -78,7 +78,7 @@ jobs: docker compose up -d sleep 10 - name: Benchmark Latest - run: oha -z 180s http://localhost/v1/health/version -j > benchmark-latest.json + run: 'oha -z 180s http://localhost/v1/health/version -o json > benchmark-latest.json' - name: Prepare comment run: | echo '## :sparkles: Benchmark results' > benchmark.txt From 6a74273564dd43ded759befa6e265dbdbe148d41 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 23 Jun 2025 12:45:34 +0530 Subject: [PATCH 168/173] fix: oha as per latest updates. --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6b159985f2..262c79ab3d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -65,7 +65,7 @@ jobs: sudo apt update sudo apt install oha - name: Benchmark PR - run: 'oha -z 180s http://localhost/v1/health/version -o json > benchmark.json' + run: 'oha -z 180s http://localhost/v1/health/version --output-format json --output benchmark.json' - name: Cleaning run: docker compose down -v - name: Installing latest version @@ -78,7 +78,7 @@ jobs: docker compose up -d sleep 10 - name: Benchmark Latest - run: 'oha -z 180s http://localhost/v1/health/version -o json > benchmark-latest.json' + run: 'oha -z 180s http://localhost/v1/health/version --output-format json --output benchmark-latest.json' - name: Prepare comment run: | echo '## :sparkles: Benchmark results' > benchmark.txt From 7f4d6233a2f8e8e80d5bbbf6b2486ef49cf60888 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 25 Jun 2025 11:00:09 +0530 Subject: [PATCH 169/173] add: validators to swagger2. --- src/Appwrite/SDK/Specification/Format/Swagger2.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 4c4016e7e7..79b26399fd 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -415,6 +415,8 @@ class Swagger2 extends Format case 'Utopia\Database\Validator\Queries': case 'Utopia\Database\Validator\Queries\Document': case 'Utopia\Database\Validator\Queries\Documents': + case 'Appwrite\Utopia\Database\Validator\Queries\Columns': + case 'Appwrite\Utopia\Database\Validator\Queries\Tables': $node['type'] = 'array'; $node['collectionFormat'] = 'multi'; $node['items'] = [ From 789064e295566e815c759066199b71f3f6214469 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 25 Jun 2025 11:29:45 +0530 Subject: [PATCH 170/173] add: new scopes to the api. --- app/config/roles.php | 8 ++++++++ app/config/scopes.php | 18 ++++++++++++++++++ app/config/templates/function.php | 18 +++++++++--------- .../Tables/Columns/Boolean/Create.php | 2 +- .../Tables/Columns/Boolean/Update.php | 2 +- .../Tables/Columns/Datetime/Create.php | 2 +- .../Tables/Columns/Datetime/Update.php | 2 +- .../Http/Databases/Tables/Columns/Delete.php | 2 +- .../Databases/Tables/Columns/Email/Create.php | 2 +- .../Databases/Tables/Columns/Email/Update.php | 2 +- .../Databases/Tables/Columns/Enum/Create.php | 2 +- .../Databases/Tables/Columns/Enum/Update.php | 2 +- .../Databases/Tables/Columns/Float/Create.php | 2 +- .../Databases/Tables/Columns/Float/Update.php | 2 +- .../Http/Databases/Tables/Columns/Get.php | 2 +- .../Databases/Tables/Columns/IP/Create.php | 2 +- .../Databases/Tables/Columns/IP/Update.php | 2 +- .../Tables/Columns/Integer/Create.php | 2 +- .../Tables/Columns/Integer/Update.php | 2 +- .../Tables/Columns/Relationship/Create.php | 2 +- .../Tables/Columns/Relationship/Update.php | 2 +- .../Databases/Tables/Columns/String/Create.php | 2 +- .../Databases/Tables/Columns/String/Update.php | 2 +- .../Databases/Tables/Columns/URL/Create.php | 2 +- .../Databases/Tables/Columns/URL/Update.php | 2 +- .../Http/Databases/Tables/Columns/XList.php | 2 +- .../Databases/Http/Databases/Tables/Create.php | 2 +- .../Databases/Http/Databases/Tables/Delete.php | 2 +- .../Databases/Http/Databases/Tables/Get.php | 2 +- .../Http/Databases/Tables/Indexes/Create.php | 2 +- .../Http/Databases/Tables/Indexes/Delete.php | 2 +- .../Http/Databases/Tables/Indexes/Get.php | 2 +- .../Http/Databases/Tables/Indexes/XList.php | 2 +- .../Http/Databases/Tables/Logs/XList.php | 2 +- .../Http/Databases/Tables/Rows/Bulk/Delete.php | 2 +- .../Http/Databases/Tables/Rows/Bulk/Update.php | 2 +- .../Http/Databases/Tables/Rows/Bulk/Upsert.php | 2 +- .../Databases/Tables/Rows/Column/Decrement.php | 2 +- .../Databases/Tables/Rows/Column/Increment.php | 2 +- .../Http/Databases/Tables/Rows/Create.php | 2 +- .../Http/Databases/Tables/Rows/Delete.php | 2 +- .../Http/Databases/Tables/Rows/Get.php | 2 +- .../Http/Databases/Tables/Rows/Logs/XList.php | 2 +- .../Http/Databases/Tables/Rows/Update.php | 2 +- .../Http/Databases/Tables/Rows/Upsert.php | 2 +- .../Http/Databases/Tables/Rows/XList.php | 2 +- .../Databases/Http/Databases/Tables/Update.php | 2 +- .../Http/Databases/Tables/Usage/Get.php | 2 +- .../Databases/Http/Databases/Tables/XList.php | 2 +- tests/e2e/Scopes/ProjectCustom.php | 4 ++++ 50 files changed, 85 insertions(+), 55 deletions(-) diff --git a/app/config/roles.php b/app/config/roles.php index bccc2837f5..4b06e0a6c1 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -14,6 +14,8 @@ $member = [ 'teams.write', 'documents.read', 'documents.write', + 'rows.read', + 'rows.write', 'files.read', 'files.write', 'projects.read', @@ -37,6 +39,8 @@ $admins = [ 'teams.write', 'documents.read', 'documents.write', + 'rows.read', + 'rows.write', 'files.read', 'files.write', 'buckets.read', @@ -47,6 +51,8 @@ $admins = [ 'databases.write', 'collections.read', 'collections.write', + 'tables.read', + 'tables.write', 'platforms.read', 'platforms.write', 'projects.write', @@ -97,6 +103,8 @@ return [ 'sessions.write', 'documents.read', 'documents.write', + 'rows.read', + 'rows.write', 'files.read', 'files.write', 'locale.read', diff --git a/app/config/scopes.php b/app/config/scopes.php index 7dea7b1cd5..d90ca2eabf 100644 --- a/app/config/scopes.php +++ b/app/config/scopes.php @@ -28,12 +28,24 @@ return [ // List of publicly visible scopes 'collections.write' => [ 'description' => 'Access to create, update, and delete your project\'s database collections', ], + 'tables.read' => [ + 'description' => 'Access to read your project\'s database tables', + ], + 'tables.write' => [ + 'description' => 'Access to create, update, and delete your project\'s database tables', + ], 'attributes.read' => [ 'description' => 'Access to read your project\'s database collection\'s attributes', ], 'attributes.write' => [ 'description' => 'Access to create, update, and delete your project\'s database collection\'s attributes', ], + 'columns.read' => [ + 'description' => 'Access to read your project\'s database table\'s columns', + ], + 'columns.write' => [ + 'description' => 'Access to create, update, and delete your project\'s database table\'s columns', + ], 'indexes.read' => [ 'description' => 'Access to read your project\'s database collection\'s indexes', ], @@ -46,6 +58,12 @@ return [ // List of publicly visible scopes 'documents.write' => [ 'description' => 'Access to create, update, and delete your project\'s database documents', ], + 'rows.read' => [ + 'description' => 'Access to read your project\'s database rows', + ], + 'rows.write' => [ + 'description' => 'Access to create, update, and delete your project\'s database rows', + ], 'files.read' => [ 'description' => 'Access to read your project\'s storage files and preview images', ], diff --git a/app/config/templates/function.php b/app/config/templates/function.php index 960bc1d65e..9a905c83b1 100644 --- a/app/config/templates/function.php +++ b/app/config/templates/function.php @@ -635,7 +635,7 @@ return [ 'type' => 'url' ] ], - 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write'] + 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'tables.write', 'attributes.write', 'columns.write', 'documents.read', 'rows.read', 'documents.write', 'rows.write'] ], [ 'icon' => 'icon-algolia', @@ -717,7 +717,7 @@ return [ 'type' => 'password' ], ], - 'scopes' => ['databases.read', 'collections.read', 'documents.read'] + 'scopes' => ['databases.read', 'collections.read', 'tables.read', 'documents.read', 'rows.read'] ], [ 'icon' => 'icon-meilisearch', @@ -811,7 +811,7 @@ return [ 'type' => 'text' ], ], - 'scopes' => ['databases.read', 'collections.read', 'documents.read'] + 'scopes' => ['databases.read', 'collections.read', 'tables.read', 'documents.read', 'rows.read'] ], [ 'icon' => 'icon-vonage', @@ -1139,7 +1139,7 @@ return [ 'type' => 'text' ] ], - 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write'] + 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'tables.write', 'attributes.write', 'columns.write', 'documents.read', 'rows.read', 'documents.write', 'rows.write'] ], [ 'icon' => 'icon-chat', @@ -1268,7 +1268,7 @@ return [ 'type' => 'password' ] ], - 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read'] + 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'tables.read', 'collections.write', 'tables.write', 'attributes.write', 'columns.write', 'documents.read', 'rows.read', 'documents.write', 'rows.write', 'buckets.read', 'buckets.write', 'files.read'] ], [ 'icon' => 'icon-eye', @@ -1327,7 +1327,7 @@ return [ 'type' => 'password' ] ], - 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read'] + 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'tables.read', 'collections.write', 'tables.write', 'attributes.write', 'columns.write', 'documents.read', 'rows.read', 'documents.write', 'rows.write', 'buckets.read', 'buckets.write', 'files.read'] ], [ 'icon' => 'icon-text', @@ -1386,7 +1386,7 @@ return [ 'type' => 'password' ] ], - 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read'] + 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'tables.read', 'collections.write', 'tables.write', 'attributes.write', 'columns.write', 'documents.read', 'rows.read', 'documents.write', 'rows.write', 'buckets.read', 'buckets.write', 'files.read'] ], [ 'icon' => 'icon-chat', @@ -1669,7 +1669,7 @@ return [ 'type' => 'text' ] ], - 'scopes' => ['databases.read', 'collections.read', 'documents.read'] + 'scopes' => ['databases.read', 'collections.read', 'tables.read', 'documents.read', 'rows.read'] ], [ 'icon' => 'icon-chip', @@ -1733,7 +1733,7 @@ return [ 'type' => 'text' ] ], - 'scopes' => ['databases.read', 'collections.read', 'documents.read'] + 'scopes' => ['databases.read', 'collections.read', 'tables.read', 'documents.read', 'rows.read'] ], [ 'icon' => 'icon-chat', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php index 9e65e6e5b3..30972e8458 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php @@ -32,7 +32,7 @@ class Create extends BooleanCreate ->desc('Create boolean column') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php index f7eff81ae3..82e7d9526d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php @@ -33,7 +33,7 @@ class Update extends BooleanUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key') ->desc('Update boolean column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php index 5bb83733a4..44e9cf9cd3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php @@ -33,7 +33,7 @@ class Create extends DatetimeCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime') ->desc('Create datetime column') ->groups(['api', 'database']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php index 6710f76aaa..27f5f3e82b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php @@ -35,7 +35,7 @@ class Update extends DatetimeUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key') ->desc('Update dateTime column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php index b85248e8c3..ce3e66a85f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php @@ -32,7 +32,7 @@ class Delete extends AttributesDelete ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') ->desc('Delete column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.delete') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php index 6aade1aa50..c497b9d78c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php @@ -32,7 +32,7 @@ class Create extends EmailCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email') ->desc('Create email column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php index 10213c87bb..267d5b80e7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php @@ -34,7 +34,7 @@ class Update extends EmailUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email/:key') ->desc('Update email column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php index 8e9a658aa8..38b7ac231a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php @@ -34,7 +34,7 @@ class Create extends EnumCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum') ->desc('Create enum column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php index d1418e5144..97785b1121 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php @@ -36,7 +36,7 @@ class Update extends EnumUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key') ->desc('Update enum column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php index 5c99b90f52..4a96165b32 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php @@ -32,7 +32,7 @@ class Create extends FloatCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float') ->desc('Create float column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php index 5360ca0304..d570ce4857 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php @@ -34,7 +34,7 @@ class Update extends FloatUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float/:key') ->desc('Update float column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php index 17777409d2..85ac43b63d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php @@ -41,7 +41,7 @@ class Get extends AttributesGet ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key') ->desc('Get column') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php index cb58a14cbe..874c0f2132 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php @@ -32,7 +32,7 @@ class Create extends IPCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip') ->desc('Create IP address column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php index ef4c62a51f..73a73bccac 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php @@ -34,7 +34,7 @@ class Update extends IPUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key') ->desc('Update IP address column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php index 6ca6cc4b93..11a36c1e3c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php @@ -32,7 +32,7 @@ class Create extends IntegerCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer') ->desc('Create integer column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php index 764ae31f35..292eb3de7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php @@ -34,7 +34,7 @@ class Update extends IntegerUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key') ->desc('Update integer column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php index 20e89396a0..583f8bb16c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php @@ -33,7 +33,7 @@ class Create extends RelationshipCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/relationship') ->desc('Create relationship column') ->groups(['api', 'database']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php index 508c5da57a..d17cf9def2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php @@ -33,7 +33,7 @@ class Update extends RelationshipUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship') ->desc('Update relationship column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php index a11ee3464e..9d86fc8c8e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php @@ -34,7 +34,7 @@ class Create extends StringCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string') ->desc('Create string column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php index 92df091866..8106ccf626 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php @@ -36,7 +36,7 @@ class Update extends StringUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string/:key') ->desc('Update string column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php index 84c488ccfc..340b120c5c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php @@ -32,7 +32,7 @@ class Create extends URLCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url') ->desc('Create URL column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php index d3681b9358..82ab8fa614 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php @@ -34,7 +34,7 @@ class Update extends URLUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url/:key') ->desc('Update URL column') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php index 8190f2ec71..b6578777b0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php @@ -30,7 +30,7 @@ class XList extends AttributesXList ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns') ->desc('List columns') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php index 0bfdef5249..b80cf4f773 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php @@ -35,7 +35,7 @@ class Create extends CollectionCreate ->desc('Create table') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].create') - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'table.create') ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php index caa7197acb..2465496023 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php @@ -30,7 +30,7 @@ class Delete extends CollectionDelete ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') ->desc('Delete table') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].delete') ->label('audits.event', 'table.delete') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php index 40672ba51f..dec3a879a4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php @@ -30,7 +30,7 @@ class Get extends CollectionGet ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') ->desc('Get table') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php index b11892e0f9..92397cd460 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php @@ -37,7 +37,7 @@ class Create extends IndexCreate ->desc('Create index') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].create') - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'index.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index 4ff9638f27..eb8e2c5833 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -35,7 +35,7 @@ class Delete extends IndexDelete ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') ->desc('Delete index') ->groups(['api', 'database']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') ->label('audits.event', 'index.delete') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php index 79529fda66..9a63fa79c3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php @@ -31,7 +31,7 @@ class Get extends IndexGet ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes/:key') ->desc('Get index') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php index 12581a4fc3..85685cdd6c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php @@ -31,7 +31,7 @@ class XList extends IndexXList ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/indexes') ->desc('List indexes') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index 8e652c5416..afe33597e2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -27,7 +27,7 @@ class XList extends CollectionLogXList ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/logs') ->desc('List table logs') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php index 945450aee3..a29d73aeee 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Delete.php @@ -32,7 +32,7 @@ class Delete extends DocumentsDelete ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Delete rows') ->groups(['api', 'database']) - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php index 916d683491..529d4b0b96 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Update.php @@ -33,7 +33,7 @@ class Update extends DocumentsUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Update rows') ->groups(['api', 'database']) - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php index 65c848a7c4..2bda5f0172 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Bulk/Upsert.php @@ -32,7 +32,7 @@ class Upsert extends DocumentsUpsert ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Create or update rows') ->groups(['api', 'database']) - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php index 916c051d8a..a0761f0bd2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Decrement.php @@ -33,7 +33,7 @@ class Decrement extends DecrementDocumentAttribute ->desc('Decrement row column') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].decrement') - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.decrement') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php index ba78833744..d28b2a3968 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Column/Increment.php @@ -33,7 +33,7 @@ class Increment extends IncrementDocumentAttribute ->desc('Increment row column') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].increment') - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.increment') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php index ba2b461d6e..bba4960b69 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php @@ -41,7 +41,7 @@ class Create extends DocumentCreate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('Create row') ->groups(['api', 'database']) - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php index 27fe101f1d..22c25d5b16 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php @@ -36,7 +36,7 @@ class Delete extends DocumentDelete ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') ->desc('Delete row') ->groups(['api', 'database']) - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].delete') ->label('audits.event', 'row.delete') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php index 8100abbea5..0266781728 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php @@ -32,7 +32,7 @@ class Get extends DocumentGet ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId') ->desc('Get row') ->groups(['api', 'database']) - ->label('scope', 'documents.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php index f39a77323f..ad42f16f44 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php @@ -27,7 +27,7 @@ class XList extends DocumentLogXList ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs') ->desc('List row logs') ->groups(['api', 'database']) - ->label('scope', 'documents.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php index 9fa596ecc0..15796c3b6b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php @@ -34,7 +34,7 @@ class Update extends DocumentUpdate ->desc('Update row') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].update') - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php index 5c0fde708b..42a79e8a72 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Upsert.php @@ -34,7 +34,7 @@ class Upsert extends DocumentUpsert ->desc('Create or update a row') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].upsert') - ->label('scope', 'documents.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.upsert') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php index b7ad763e36..7e942f4842 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php @@ -32,7 +32,7 @@ class XList extends DocumentXList ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows') ->desc('List rows') ->groups(['api', 'database']) - ->label('scope', 'documents.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php index f800e7b1de..b19dc143b9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php @@ -33,7 +33,7 @@ class Update extends CollectionUpdate ->setHttpPath('/v1/databases/:databaseId/tables/:tableId') ->desc('Update table') ->groups(['api', 'database', 'schema']) - ->label('scope', 'collections.write') + ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].update') ->label('audits.event', 'table.update') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php index ca990eeab6..cede0762d8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php @@ -31,7 +31,7 @@ class Get extends CollectionUsageGet ->setHttpPath('/v1/databases/:databaseId/tables/:tableId/usage') ->desc('Get table usage stats') ->groups(['api', 'database', 'usage']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php index 34000d97b1..24034f7f0a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php @@ -32,7 +32,7 @@ class XList extends CollectionXList ->setHttpPath('/v1/databases/:databaseId/tables') ->desc('List tables') ->groups(['api', 'database']) - ->label('scope', 'collections.read') + ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: $this->getSdkNamespace(), diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 51aebeaef7..ea27318390 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -70,8 +70,12 @@ trait ProjectCustom 'databases.write', 'collections.read', 'collections.write', + 'tables.read', + 'tables.write', 'documents.read', 'documents.write', + 'rows.read', + 'rows.write', 'files.read', 'files.write', 'buckets.read', From d7d8dbdbd47c1b0788c13232f9e264bfd045dcb6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 25 Jun 2025 11:53:21 +0530 Subject: [PATCH 171/173] fix: name. --- .../Modules/Databases/Http/Databases/Tables/Indexes/Delete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php index eb8e2c5833..0fec37eec3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php @@ -16,7 +16,7 @@ class Delete extends IndexDelete { public static function getName(): string { - return 'updateColumnIndex'; + return 'deleteColumnIndex'; } /** From bce666e81b8fceb418a6a1a7e770f126368c388d Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 25 Jun 2025 17:02:21 +0530 Subject: [PATCH 172/173] add: scopes on migrations and update tests. --- src/Appwrite/Platform/Workers/Migrations.php | 3 +++ tests/e2e/Services/Projects/ProjectsConsoleClientTest.php | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index b59308c288..66f45004ea 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -246,8 +246,11 @@ class Migrations extends Action 'functions.write', 'databases.read', 'collections.read', + 'tables.read', 'documents.read', 'documents.write', + 'rows.read', + 'rows.write', 'tokens.read', 'tokens.write', ] diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 8b34d552f5..5e22a28016 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -2991,7 +2991,7 @@ class ProjectsConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'name' => 'Key Test Update', - 'scopes' => ['users.read', 'users.write', 'collections.read'], + 'scopes' => ['users.read', 'users.write', 'collections.read', 'tables.read'], 'expire' => DateTime::addSeconds(new \DateTime(), 360), ]); @@ -3002,6 +3002,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertContains('users.read', $response['body']['scopes']); $this->assertContains('users.write', $response['body']['scopes']); $this->assertContains('collections.read', $response['body']['scopes']); + $this->assertContains('tables.read', $response['body']['scopes']); $this->assertCount(3, $response['body']['scopes']); $this->assertArrayHasKey('sdks', $response['body']); $this->assertEmpty($response['body']['sdks']); @@ -3020,6 +3021,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertContains('users.read', $response['body']['scopes']); $this->assertContains('users.write', $response['body']['scopes']); $this->assertContains('collections.read', $response['body']['scopes']); + $this->assertContains('tables.read', $response['body']['scopes']); $this->assertCount(3, $response['body']['scopes']); $this->assertArrayHasKey('sdks', $response['body']); $this->assertEmpty($response['body']['sdks']); From 67e546faa8d0bb6f4443a25a3ce2c246344f9289 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 25 Jun 2025 17:50:54 +0530 Subject: [PATCH 173/173] fix: namespace for table logs. --- .../Modules/Databases/Http/Databases/Tables/Logs/XList.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php index afe33597e2..d18cae9d6d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php @@ -30,9 +30,9 @@ class XList extends CollectionLogXList ->label('scope', 'tables.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: 'databases', + namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), - name: self::getName(), + name: 'listLogs', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/databases/get-collection-logs.md', auth: [AuthType::ADMIN], responses: [