From 5c09d726baee3b702be2130863a2e9a930d489a9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 11 May 2026 19:51:26 +1200 Subject: [PATCH 1/8] fix(messaging): remove redundant user fetch --- src/Appwrite/Platform/Workers/Messaging.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 03adebc4b5..2d56f3b18d 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -162,17 +162,13 @@ class Messaging extends Action } if (\count($userIds) > 0) { - $users = $dbForProject->find('users', [ - Query::equal('$id', $userIds), + $targets = $dbForProject->find('targets', [ + Query::equal('userId', $userIds), + Query::equal('providerType', [$providerType]), Query::limit(\count($userIds)), ]); - foreach ($users as $user) { - $targets = \array_filter($user->getAttribute('targets'), function (Document $target) use ($providerType) { - return $target->getAttribute('providerType') === $providerType; - }); - \array_push($allTargets, ...$targets); - } + \array_push($allTargets, ...$targets); } if (\count($targetIds) > 0) { From add56d73658cd015e49e02c34afed37c5c769900 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 11 May 2026 19:54:48 +1200 Subject: [PATCH 2/8] chore: select --- src/Appwrite/Platform/Workers/Messaging.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 2d56f3b18d..eabf5abca2 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -164,6 +164,7 @@ class Messaging extends Action if (\count($userIds) > 0) { $targets = $dbForProject->find('targets', [ Query::equal('userId', $userIds), + Query::select(['identifier']), Query::equal('providerType', [$providerType]), Query::limit(\count($userIds)), ]); @@ -173,6 +174,7 @@ class Messaging extends Action if (\count($targetIds) > 0) { $targets = $dbForProject->find('targets', [ + Query::select(['providerId', 'identifier']), Query::equal('$id', $targetIds), Query::equal('providerType', [$providerType]), Query::limit(\count($targetIds)), From 51f1aee93a61db08009a5249b71f088ab115b00c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 11 May 2026 21:50:38 +1200 Subject: [PATCH 3/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/Appwrite/Platform/Workers/Messaging.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index eabf5abca2..e928489dce 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -166,7 +166,6 @@ class Messaging extends Action Query::equal('userId', $userIds), Query::select(['identifier']), Query::equal('providerType', [$providerType]), - Query::limit(\count($userIds)), ]); \array_push($allTargets, ...$targets); From 3773a5a5dcbedace05be5296965b812c84089ce3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 11 May 2026 21:51:19 +1200 Subject: [PATCH 4/8] Update src/Appwrite/Platform/Workers/Messaging.php Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/Appwrite/Platform/Workers/Messaging.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index e928489dce..188547843d 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -161,11 +161,11 @@ class Messaging extends Action } } - if (\count($userIds) > 0) { $targets = $dbForProject->find('targets', [ Query::equal('userId', $userIds), - Query::select(['identifier']), + Query::select(['providerId', 'identifier']), Query::equal('providerType', [$providerType]), + Query::limit(\count($userIds)), ]); \array_push($allTargets, ...$targets); From 2a47ccc0f944e54571234b660b348644ef44e399 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 10:03:02 +0000 Subject: [PATCH 5/8] test(messaging): add users recipient e2e coverage and fix user target query limit Agent-Logs-Url: https://github.com/appwrite/appwrite/sessions/8ea97768-3304-4a3c-81e2-d62d6fe3ad80 Co-authored-by: abnegate <5857008+abnegate@users.noreply.github.com> --- src/Appwrite/Platform/Workers/Messaging.php | 2 +- .../e2e/Services/Messaging/MessagingBase.php | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 188547843d..e23c235221 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -161,11 +161,11 @@ class Messaging extends Action } } + if (\count($userIds) > 0) { $targets = $dbForProject->find('targets', [ Query::equal('userId', $userIds), Query::select(['providerId', 'identifier']), Query::equal('providerType', [$providerType]), - Query::limit(\count($userIds)), ]); \array_push($allTargets, ...$targets); diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index d83b450739..76c66bfe6b 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -2525,6 +2525,115 @@ trait MessagingBase $this->assertEquals(0, \count($message['body']['deliveryErrors'])); } + public function testCreatePushNotificationWithUsersRecipients(): void + { + $dsn = new DSN(System::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); + $to = $dsn->getParam('to'); + $serviceAccountJSON = $dsn->getParam('serviceAccountJSON'); + + if (empty($to) || empty($serviceAccountJSON)) { + $this->markTestSkipped('Push provider not configured'); + } + + $provider1 = $this->client->call(Client::METHOD_POST, '/messaging/providers/fcm', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'providerId' => ID::unique(), + 'name' => 'FCM-Users-1', + 'serviceAccountJSON' => $serviceAccountJSON, + 'enabled' => true, + ]); + + $this->assertEquals(201, $provider1['headers']['status-code']); + + $provider2 = $this->client->call(Client::METHOD_POST, '/messaging/providers/fcm', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'providerId' => ID::unique(), + 'name' => 'FCM-Users-2', + 'serviceAccountJSON' => $serviceAccountJSON, + 'enabled' => true, + ]); + + $this->assertEquals(201, $provider2['headers']['status-code']); + + $user = $this->client->call(Client::METHOD_POST, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'userId' => ID::unique(), + 'email' => uniqid() . "@mail.org", + 'password' => 'password', + 'name' => 'Messaging User Recipients', + ]); + + $this->assertEquals(201, $user['headers']['status-code']); + + $target1 = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'targetId' => ID::unique(), + 'providerType' => 'push', + 'providerId' => $provider1['body']['$id'], + 'identifier' => $to, + ]); + + $this->assertEquals(201, $target1['headers']['status-code']); + + $target2 = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'targetId' => ID::unique(), + 'providerType' => 'push', + 'providerId' => $provider2['body']['$id'], + 'identifier' => $to, + ]); + + $this->assertEquals(201, $target2['headers']['status-code']); + + $push = $this->client->call(Client::METHOD_POST, '/messaging/messages/push', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'messageId' => ID::unique(), + 'users' => [$user['body']['$id']], + 'title' => 'Test-Notification-Users', + 'body' => 'Test-Notification-Body-Users', + ]); + + $this->assertEquals(201, $push['headers']['status-code']); + + $pushMessageId = $push['body']['$id']; + $this->assertEventually(function () use ($pushMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $pushMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); + + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $pushMessageId, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $message['headers']['status-code']); + $this->assertEquals(2, $message['body']['deliveredTotal'] + \count($message['body']['deliveryErrors'])); + } + public function testUpdatePushNotification(): void { $push = $this->setupSentPushData(); From d8957a546844b922432103004c08ba25bca0dad8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 10:03:40 +0000 Subject: [PATCH 6/8] fix(messaging): paginate user recipient target lookup Agent-Logs-Url: https://github.com/appwrite/appwrite/sessions/8ea97768-3304-4a3c-81e2-d62d6fe3ad80 Co-authored-by: abnegate <5857008+abnegate@users.noreply.github.com> --- src/Appwrite/Platform/Workers/Messaging.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index e23c235221..aa2dd91598 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -162,13 +162,21 @@ class Messaging extends Action } if (\count($userIds) > 0) { - $targets = $dbForProject->find('targets', [ - Query::equal('userId', $userIds), - Query::select(['providerId', 'identifier']), - Query::equal('providerType', [$providerType]), - ]); + $limit = 100; + $offset = 0; - \array_push($allTargets, ...$targets); + do { + $targets = $dbForProject->find('targets', [ + Query::equal('userId', $userIds), + Query::select(['providerId', 'identifier']), + Query::equal('providerType', [$providerType]), + Query::limit($limit), + Query::offset($offset), + ]); + + \array_push($allTargets, ...$targets); + $offset += \count($targets); + } while (\count($targets) === $limit); } if (\count($targetIds) > 0) { From e6a93d4a9fad9944f02966a1614df1bfe0c9a8a6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 11 May 2026 22:37:44 +1200 Subject: [PATCH 7/8] chore: bump limit --- src/Appwrite/Platform/Workers/Messaging.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index aa2dd91598..649fab5233 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -162,7 +162,7 @@ class Messaging extends Action } if (\count($userIds) > 0) { - $limit = 100; + $limit = 1000; $offset = 0; do { From d2e4ed6a11d99a124a0dc271d4bdfefa8640513e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 11 May 2026 22:58:29 +1200 Subject: [PATCH 8/8] test(messaging): skip users recipient test when push DSN not configured CI lacks _APP_MESSAGE_PUSH_TEST_DSN, so new DSN(null) threw a TypeError before the existing skip guard could run. Match the pattern used by testSendPushNotification: gate on the env var first, then construct the DSN. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/e2e/Services/Messaging/MessagingBase.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index 76c66bfe6b..43dc58d44c 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -2527,6 +2527,10 @@ trait MessagingBase public function testCreatePushNotificationWithUsersRecipients(): void { + if (empty(System::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { + $this->markTestSkipped('Push DSN empty'); + } + $dsn = new DSN(System::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); $to = $dsn->getParam('to'); $serviceAccountJSON = $dsn->getParam('serviceAccountJSON');