Skip to content

Commit 400eb89

Browse files
authored
Merge pull request #646 from utopia-php/fix-update-documents
Fix update documents
2 parents 8fa21e9 + 7c1eba7 commit 400eb89

File tree

3 files changed

+187
-6
lines changed

3 files changed

+187
-6
lines changed

src/Database/Adapter/SQL.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ public function updateDocuments(string $collection, Document $updates, array $do
426426
$attributes['_createdAt'] = $updates->getCreatedAt();
427427
}
428428

429-
if (!empty($updates->getPermissions())) {
429+
if ($updates->offsetExists('$permissions')) {
430430
$attributes['_permissions'] = json_encode($updates->getPermissions());
431431
}
432432

@@ -484,15 +484,18 @@ public function updateDocuments(string $collection, Document $updates, array $do
484484
$affected = $stmt->rowCount();
485485

486486
// Permissions logic
487-
if (!empty($updates->getPermissions())) {
487+
if ($updates->offsetExists('$permissions')) {
488488
$removeQueries = [];
489489
$removeBindValues = [];
490490

491491
$addQuery = '';
492492
$addBindValues = [];
493493

494494
foreach ($documents as $index => $document) {
495-
// Permissions logic
495+
if ($document->getAttribute('$skipPermissionsUpdate', false)) {
496+
continue;
497+
}
498+
496499
$sql = "
497500
SELECT _type, _permission
498501
FROM {$this->getSQLTable($name . '_perms')}

src/Database/Database.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4385,13 +4385,16 @@ public function updateDocuments(
43854385
if (!empty($cursor) && $cursor->getCollection() !== $collection->getId()) {
43864386
throw new DatabaseException("Cursor document must be from the same Collection.");
43874387
}
4388+
43884389
unset($updates['$id']);
43894390
unset($updates['$tenant']);
4391+
43904392
if (($updates->getCreatedAt() === null || !$this->preserveDates)) {
43914393
unset($updates['$createdAt']);
43924394
} else {
43934395
$updates['$createdAt'] = $updates->getCreatedAt();
43944396
}
4397+
43954398
if ($this->adapter->getSharedTables()) {
43964399
$updates['$tenant'] = $this->adapter->getTenant();
43974400
}
@@ -4441,8 +4444,27 @@ public function updateDocuments(
44414444
break;
44424445
}
44434446

4444-
$this->withTransaction(function () use ($collection, $updates, &$batch) {
4445-
foreach ($batch as &$document) {
4447+
$currentPermissions = $updates->getPermissions();
4448+
sort($currentPermissions);
4449+
4450+
$this->withTransaction(function () use ($collection, $updates, &$batch, $currentPermissions) {
4451+
foreach ($batch as $index => $document) {
4452+
4453+
$skipPermissionsUpdate = true;
4454+
4455+
if ($updates->offsetExists('$permissions')) {
4456+
if (!$document->offsetExists('$permissions')) {
4457+
throw new QueryException('Permission document missing in select');
4458+
}
4459+
4460+
$originalPermissions = $document->getPermissions();
4461+
sort($originalPermissions);
4462+
4463+
$skipPermissionsUpdate = ($originalPermissions === $currentPermissions);
4464+
}
4465+
4466+
$document->setAttribute('$skipPermissionsUpdate', $skipPermissionsUpdate);
4467+
44464468
$new = new Document(\array_merge($document->getArrayCopy(), $updates->getArrayCopy()));
44474469

44484470
if ($this->resolveRelationships) {
@@ -4462,7 +4484,7 @@ public function updateDocuments(
44624484
throw new ConflictException('Document was updated after the request timestamp');
44634485
}
44644486

4465-
$document = $this->encode($collection, $document);
4487+
$batch[$index] = $this->encode($collection, $document);
44664488
}
44674489

44684490
$this->adapter->updateDocuments(
@@ -4473,6 +4495,8 @@ public function updateDocuments(
44734495
});
44744496

44754497
foreach ($batch as $doc) {
4498+
$doc->removeAttribute('$skipPermissionsUpdate');
4499+
44764500
$this->purgeCachedDocument($collection->getId(), $doc->getId());
44774501
$doc = $this->decode($collection, $doc);
44784502
try {

tests/e2e/Adapter/Scopes/PermissionTests.php

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,160 @@
1414

1515
trait PermissionTests
1616
{
17+
public function testUnsetPermissions(): void
18+
{
19+
/** @var Database $database */
20+
$database = static::getDatabase();
21+
22+
$database->createCollection(__FUNCTION__);
23+
$this->assertTrue($database->createAttribute(
24+
collection: __FUNCTION__,
25+
id: 'president',
26+
type: Database::VAR_STRING,
27+
size: 255,
28+
required: false
29+
));
30+
31+
$permissions = [
32+
Permission::read(Role::any()),
33+
Permission::create(Role::any()),
34+
Permission::update(Role::any()),
35+
Permission::delete(Role::any()),
36+
];
37+
38+
$documents = [];
39+
40+
for ($i = 0; $i < 3; $i++) {
41+
$documents[] = new Document([
42+
'$permissions' => $permissions,
43+
'president' => 'Donald Trump'
44+
]);
45+
}
46+
47+
$results = [];
48+
$count = $database->createDocuments(__FUNCTION__, $documents, onNext: function ($doc) use (&$results) {
49+
$results[] = $doc;
50+
});
51+
52+
$this->assertEquals(3, $count);
53+
54+
foreach ($results as $result) {
55+
$this->assertEquals('Donald Trump', $result->getAttribute('president'));
56+
$this->assertEquals($permissions, $result->getPermissions());
57+
}
58+
59+
/**
60+
* No permissions passed, Check old is preserved
61+
*/
62+
$updates = new Document([
63+
'president' => 'George Washington'
64+
]);
65+
66+
$results = [];
67+
$modified = $database->updateDocuments(
68+
__FUNCTION__,
69+
$updates,
70+
onNext: function ($doc) use (&$results) {
71+
$results[] = $doc;
72+
}
73+
);
74+
75+
$this->assertEquals(3, $modified);
76+
77+
foreach ($results as $result) {
78+
$this->assertEquals('George Washington', $result->getAttribute('president'));
79+
$this->assertEquals($permissions, $result->getPermissions());
80+
}
81+
82+
$documents = $database->find(__FUNCTION__);
83+
84+
$this->assertEquals(3, count($documents));
85+
86+
foreach ($documents as $document) {
87+
$this->assertEquals('George Washington', $document->getAttribute('president'));
88+
$this->assertEquals($permissions, $document->getPermissions());
89+
}
90+
91+
/**
92+
* Change permissions remove delete
93+
*/
94+
$permissions = [
95+
Permission::read(Role::any()),
96+
Permission::create(Role::any()),
97+
Permission::update(Role::any()),
98+
];
99+
100+
$updates = new Document([
101+
'$permissions' => $permissions,
102+
'president' => 'Joe biden'
103+
]);
104+
105+
$results = [];
106+
$modified = $database->updateDocuments(
107+
__FUNCTION__,
108+
$updates,
109+
onNext: function ($doc) use (&$results) {
110+
$results[] = $doc;
111+
}
112+
);
113+
114+
$this->assertEquals(3, $modified);
115+
116+
foreach ($results as $result) {
117+
$this->assertEquals('Joe biden', $result->getAttribute('president'));
118+
$this->assertEquals($permissions, $result->getPermissions());
119+
$this->assertArrayNotHasKey('$skipPermissionsUpdate', $result);
120+
}
121+
122+
$documents = $database->find(__FUNCTION__);
123+
124+
$this->assertEquals(3, count($documents));
125+
126+
foreach ($documents as $document) {
127+
$this->assertEquals('Joe biden', $document->getAttribute('president'));
128+
$this->assertEquals($permissions, $document->getPermissions());
129+
}
130+
131+
/**
132+
* Unset permissions
133+
*/
134+
$updates = new Document([
135+
'$permissions' => [],
136+
'president' => 'Richard Nixon'
137+
]);
138+
139+
$results = [];
140+
$modified = $database->updateDocuments(
141+
__FUNCTION__,
142+
$updates,
143+
onNext: function ($doc) use (&$results) {
144+
$results[] = $doc;
145+
}
146+
);
147+
148+
$this->assertEquals(3, $modified);
149+
150+
foreach ($results as $result) {
151+
$this->assertEquals('Richard Nixon', $result->getAttribute('president'));
152+
$this->assertEquals([], $result->getPermissions());
153+
}
154+
155+
$documents = $database->find(__FUNCTION__);
156+
$this->assertEquals(0, count($documents));
157+
158+
Authorization::disable();
159+
$documents = $database->find(__FUNCTION__);
160+
Authorization::reset();
161+
162+
$this->assertEquals(3, count($documents));
163+
164+
foreach ($documents as $document) {
165+
$this->assertEquals('Richard Nixon', $document->getAttribute('president'));
166+
$this->assertEquals([], $document->getPermissions());
167+
$this->assertArrayNotHasKey('$skipPermissionsUpdate', $document);
168+
}
169+
}
170+
17171
public function testCreateDocumentsEmptyPermission(): void
18172
{
19173
/** @var Database $database */

0 commit comments

Comments
 (0)