diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 358da9a527..abd22d3faf 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1859,7 +1859,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('collectionId', null, new UID(), 'Collection ID.') ->param('documentId', null, new UID(), 'Document ID.') - ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.') + ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) ->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) ->inject('response') @@ -1900,8 +1900,8 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array - if (empty($data)) { - throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD); + if (empty($data) && empty($read) && empty($write)) { + throw new Exception('Missing payload or read/write permissions', 400, Exception::DOCUMENT_MISSING_PAYLOAD); } if (!\is_array($data)) { diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 47aa3ce351..ad1d018abc 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -2181,4 +2181,106 @@ trait DatabaseBase return $data; } + + public function testUpdatePermissionsWithEmptyPayload(): array + { + // Create collection + $movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => 'unique()', + 'name' => 'Movies', + 'read' => [], + 'write' => [], + 'permission' => 'document', + ]); + + $this->assertEquals($movies['headers']['status-code'], 201); + $this->assertEquals($movies['body']['name'], 'Movies'); + + $moviesId = $movies['body']['$id']; + + // create attribute + $title = $this->client->call(Client::METHOD_POST, '/database/collections/' . $moviesId . '/attributes/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($title['headers']['status-code'], 201); + + // wait for database worker to create attributes + sleep(2); + + // add document + $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $moviesId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Captain America', + ], + 'read' => ['role:all'], + 'write' => ['role:all'], + ]); + + $id = $document['body']['$id']; + + $this->assertEquals($document['headers']['status-code'], 201); + $this->assertCount(1, $document['body']['$read']); + $this->assertCount(1, $document['body']['$write']); + $this->assertEquals(['role:all'], $document['body']['$read']); + $this->assertEquals(['role:all'], $document['body']['$write']); + + // Send only read permission + $document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $moviesId . '/documents/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'read' => ['user:' . $this->getUser()['$id']], + ]); + + if ($this->getSide() == 'client') { + $this->assertEquals($document['headers']['status-code'], 200); + } + + if ($this->getSide() == 'server') { + $this->assertEquals($document['headers']['status-code'], 200); + $this->assertCount(1, $document['body']['$read']); + $this->assertCount(1, $document['body']['$write']); + $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']); + $this->assertEquals(['role:all'], $document['body']['$write']); + } + + // send only write permission + $document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $moviesId . '/documents/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'write' => ['user:' . $this->getUser()['$id']], + ]); + + if ($this->getSide() == 'server') { + $this->assertEquals($document['headers']['status-code'], 200); + $this->assertCount(1, $document['body']['$read']); + $this->assertCount(1, $document['body']['$write']); + $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']); + $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$write']); + } + + // remove collection + $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $moviesId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return []; + } }