From 95dd44115d98aa9bb856f8e76a1d347ffed06577 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 12 May 2026 15:49:59 +0530 Subject: [PATCH] refactor: update Dockerfile to remove composer stage and adjust presence handling in tests --- app/config/collections/projects.php | 1 - src/Appwrite/Databases/PresenceState.php | 9 +++++++-- .../Presences/PresenceRealtimeClientTest.php | 13 +++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index ff723d2345..75f7b5508c 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2815,7 +2815,6 @@ return [ 'array' => false, 'filters' => [], ], - // TODO: the current pod? Shall we make it the region? instead of the hostname? So that when its restarted it can delete all the presence? [ '$id' => ID::custom('hostname'), 'type' => Database::VAR_STRING, diff --git a/src/Appwrite/Databases/PresenceState.php b/src/Appwrite/Databases/PresenceState.php index bb0f218a85..6342ca8dd5 100644 --- a/src/Appwrite/Databases/PresenceState.php +++ b/src/Appwrite/Databases/PresenceState.php @@ -69,9 +69,14 @@ class PresenceState try { if ($dbForProject->getAdapter()->getSupportForUpsertOnUniqueIndex()) { - // $id will not be updated if new id provided. id is always stable + // in v2 use permsmd5 in the queries as well to find the doc + $existingPresence = $dbForProject->findOne(self::COLLECTION_ID, [Query::equal('userInternalId', [$userInternalId])]); + if ($existingPresence->isEmpty()) { + $presenceCreated = true; + } else { + $presenceDocument->setAttribute('$id', $existingPresence->getId()); + } $presence = $dbForProject->upsertDocument(self::COLLECTION_ID, $presenceDocument); - $presenceCreated = $presence->getCreatedAt() === $presence->getUpdatedAt(); } else { $presence = $this->transactionalUpsertForUser( $dbForProject, diff --git a/tests/e2e/Services/Presences/PresenceRealtimeClientTest.php b/tests/e2e/Services/Presences/PresenceRealtimeClientTest.php index ea82f27c3c..f3e4aac1a7 100644 --- a/tests/e2e/Services/Presences/PresenceRealtimeClientTest.php +++ b/tests/e2e/Services/Presences/PresenceRealtimeClientTest.php @@ -342,7 +342,9 @@ class PresenceRealtimeClientTest extends Scope $secondMetadata, $this->getPresencePermissions(Role::any()) ); - $this->collectPresenceOutcome($publisher, $secondPresenceId, 'busy', $secondMetadata, $user['$id']); + // The server keeps one row per user keyed by userInternalId and anchors $id to the + // first claim, so the second upsert's response/event come back under $firstPresenceId. + $this->collectPresenceOutcome($publisher, $firstPresenceId, 'busy', $secondMetadata, $user['$id']); $list = $this->client->call( Client::METHOD_GET, @@ -543,12 +545,15 @@ class PresenceRealtimeClientTest extends Scope $metadataOwner, $this->getPresencePermissions(Role::user($user1['$id'])) ); - $this->collectPresenceOutcome($publisher, $presenceIdOwner, 'busy', $metadataOwner, $user1['$id']); - $this->receivePresenceEvent($listener1, $presenceIdOwner, 'upsert', 'busy', $metadataOwner, $user1['$id']); + // Same user, so the server reuses the original record's $id ($presenceIdAny); + // only permissions/status/metadata change — which is what permission routing should filter on. + $this->collectPresenceOutcome($publisher, $presenceIdAny, 'busy', $metadataOwner, $user1['$id']); + $this->receivePresenceEvent($listener1, $presenceIdAny, 'upsert', 'busy', $metadataOwner, $user1['$id']); $this->assertQuietFor( $listener2, fn (array $frame): bool => ($frame['type'] ?? null) === 'event' - && ($frame['data']['payload']['$id'] ?? null) === $presenceIdOwner + && ($frame['data']['payload']['$id'] ?? null) === $presenceIdAny + && ($frame['data']['payload']['metadata']['visibility'] ?? null) === 'owner' ); } finally { $publisher->close();