From 2e05844ca2242feca29d0afece230b483b020a64 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 13 Jun 2025 14:04:23 +0530 Subject: [PATCH] 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']);