mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 08:58:35 +00:00
Merge pull request #10184 from ArnabChatterjee20k/upsert-fix-1.8.x
Upsert fix 1.8.x
This commit is contained in:
commit
b02fc55ea2
4 changed files with 330 additions and 5 deletions
|
|
@ -77,13 +77,14 @@ class Upsert extends Action
|
|||
->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('user')
|
||||
->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
|
||||
public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void
|
||||
{
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
|
|
@ -104,12 +105,28 @@ class Upsert extends Action
|
|||
throw new Exception($this->getParentNotFoundException());
|
||||
}
|
||||
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions, [
|
||||
$allowedPermissions = [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
]);
|
||||
];
|
||||
|
||||
$permissions = Permission::aggregate($permissions, $allowedPermissions);
|
||||
// if no permission, upsert permission from the old document if present (update scenario) else add default permission (create scenario)
|
||||
if (\is_null($permissions)) {
|
||||
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId));
|
||||
if ($oldDocument->isEmpty()) {
|
||||
if (!empty($user->getId())) {
|
||||
$defaultPermissions = [];
|
||||
foreach ($allowedPermissions as $permission) {
|
||||
$defaultPermissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
|
||||
}
|
||||
$permissions = $defaultPermissions;
|
||||
}
|
||||
} else {
|
||||
$permissions = $oldDocument->getPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
|
|
@ -135,7 +152,6 @@ class Upsert extends Action
|
|||
$data['$id'] = $documentId;
|
||||
$data['$permissions'] = $permissions ?? [];
|
||||
$newDocument = new Document($data);
|
||||
|
||||
$operations = 0;
|
||||
|
||||
$setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, &$operations) {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class Upsert extends DocumentUpsert
|
|||
->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('user')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
|
|
|
|||
|
|
@ -2008,6 +2008,160 @@ trait DatabasesBase
|
|||
]));
|
||||
|
||||
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
|
||||
|
||||
if ($this->getSide() === 'client') {
|
||||
// Skipped on server side: Creating a document with no permissions results in an empty permissions array, whereas on client side it assigns permissions to the current user
|
||||
|
||||
// test without passing permissions
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2000
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertCount(3, $document['body']['$permissions']);
|
||||
$permissionsCreated = $document['body']['$permissions'];
|
||||
// checking the default created permission
|
||||
$defaultPermission = [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id']))
|
||||
];
|
||||
// ignoring the order of the permission and checking the permissions
|
||||
$this->assertEqualsCanonicalizing($defaultPermission, $permissionsCreated);
|
||||
|
||||
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
|
||||
// updating the created doc
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2002
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertEquals(2002, $document['body']['releaseYear']);
|
||||
$this->assertCount(3, $document['body']['$permissions']);
|
||||
$this->assertEquals($permissionsCreated, $document['body']['$permissions']);
|
||||
|
||||
// removing the delete permission
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2002
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::update(Role::user($this->getUser()['$id']))
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertEquals(2002, $document['body']['releaseYear']);
|
||||
$this->assertCount(1, $document['body']['$permissions']);
|
||||
|
||||
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(401, $deleteResponse['headers']['status-code']);
|
||||
|
||||
// giving the delete permission
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2002
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id']))
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertEquals(2002, $document['body']['releaseYear']);
|
||||
$this->assertCount(2, $document['body']['$permissions']);
|
||||
|
||||
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
|
||||
|
||||
// upsertion for the related document without passing permissions
|
||||
// data should get added
|
||||
$newPersonId = ID::unique();
|
||||
$personNoPerm = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/documents/' . $newPersonId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'library' => [
|
||||
'$id' => 'library3',
|
||||
'libraryName' => 'Library 3',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals('Library 3', $personNoPerm['body']['library']['libraryName']);
|
||||
$this->assertCount(3, $personNoPerm['body']['library']['$permissions']);
|
||||
$this->assertCount(3, $personNoPerm['body']['$permissions']);
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $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->assertGreaterThanOrEqual(1, $documents['body']['total']);
|
||||
$documentsDetails = $documents['body']['documents'];
|
||||
foreach ($documentsDetails as $doc) {
|
||||
$this->assertCount(3, $doc['$permissions']);
|
||||
}
|
||||
$found = false;
|
||||
foreach ($documents['body']['documents'] as $doc) {
|
||||
if (isset($doc['library']['libraryName']) && $doc['library']['libraryName'] === 'Library 3') {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found, 'Library 3 should be present in the upserted documents.');
|
||||
|
||||
// Fetch the related library and assert on its permissions (should be default/inherited)
|
||||
$library3 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $library['body']['$id'] . '/documents/library3', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $library3['headers']['status-code']);
|
||||
$this->assertEquals('Library 3', $library3['body']['libraryName']);
|
||||
$this->assertArrayHasKey('$permissions', $library3['body']);
|
||||
$this->assertCount(3, $library3['body']['$permissions']);
|
||||
$this->assertNotEmpty($library3['body']['$permissions']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1997,6 +1997,160 @@ trait DatabasesBase
|
|||
]));
|
||||
|
||||
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
|
||||
|
||||
if ($this->getSide() === 'client') {
|
||||
// Skipped on server side: Creating a document with no permissions results in an empty permissions array, whereas on client side it assigns permissions to the current user
|
||||
|
||||
// test without passing permissions
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2000
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertCount(3, $document['body']['$permissions']);
|
||||
$permissionsCreated = $document['body']['$permissions'];
|
||||
// checking the default created permission
|
||||
$defaultPermission = [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id']))
|
||||
];
|
||||
// ignoring the order of the permission and checking the permissions
|
||||
$this->assertEqualsCanonicalizing($defaultPermission, $permissionsCreated);
|
||||
|
||||
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
|
||||
// updating the created doc
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2002
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertEquals(2002, $document['body']['releaseYear']);
|
||||
$this->assertCount(3, $document['body']['$permissions']);
|
||||
$this->assertEquals($permissionsCreated, $document['body']['$permissions']);
|
||||
|
||||
// removing the delete permission
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2002
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::update(Role::user($this->getUser()['$id']))
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertEquals(2002, $document['body']['releaseYear']);
|
||||
$this->assertCount(1, $document['body']['$permissions']);
|
||||
|
||||
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(401, $deleteResponse['headers']['status-code']);
|
||||
|
||||
// giving the delete permission
|
||||
$document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
'releaseYear' => 2002
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id']))
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $document['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $document['body']['title']);
|
||||
$this->assertEquals(2002, $document['body']['releaseYear']);
|
||||
$this->assertCount(2, $document['body']['$permissions']);
|
||||
|
||||
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
|
||||
|
||||
// upsertion for the related document without passing permissions
|
||||
// data should get added
|
||||
$newPersonId = ID::unique();
|
||||
$personNoPerm = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/documents/' . $newPersonId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'library' => [
|
||||
'$id' => 'library3',
|
||||
'libraryName' => 'Library 3',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals('Library 3', $personNoPerm['body']['library']['libraryName']);
|
||||
$this->assertCount(3, $personNoPerm['body']['library']['$permissions']);
|
||||
$this->assertCount(3, $personNoPerm['body']['$permissions']);
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $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->assertGreaterThanOrEqual(1, $documents['body']['total']);
|
||||
$documentsDetails = $documents['body']['documents'];
|
||||
foreach ($documentsDetails as $doc) {
|
||||
$this->assertCount(3, $doc['$permissions']);
|
||||
}
|
||||
$found = false;
|
||||
foreach ($documents['body']['documents'] as $doc) {
|
||||
if (isset($doc['library']['libraryName']) && $doc['library']['libraryName'] === 'Library 3') {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found, 'Library 3 should be present in the upserted documents.');
|
||||
|
||||
// Fetch the related library and assert on its permissions (should be default/inherited)
|
||||
$library3 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $library['body']['$id'] . '/documents/library3', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $library3['headers']['status-code']);
|
||||
$this->assertEquals('Library 3', $library3['body']['libraryName']);
|
||||
$this->assertArrayHasKey('$permissions', $library3['body']);
|
||||
$this->assertCount(3, $library3['body']['$permissions']);
|
||||
$this->assertNotEmpty($library3['body']['$permissions']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue