diff options
-rw-r--r-- | lib/Notification/Notifier.php | 88 | ||||
-rw-r--r-- | tests/php/Notification/NotifierTest.php | 123 |
2 files changed, 198 insertions, 13 deletions
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php index c336103e6..bdacd49cb 100644 --- a/lib/Notification/Notifier.php +++ b/lib/Notification/Notifier.php @@ -73,6 +73,11 @@ class Notifier implements INotifier { /** @var Definitions */ protected $definitions; + /** @var Room[] */ + protected $rooms = []; + /** @var Participant[][] */ + protected $participants = []; + public function __construct(IFactory $lFactory, IURLGenerator $url, Config $config, @@ -118,6 +123,65 @@ class Notifier implements INotifier { } /** + * @param string $objectId + * @return Room + * @throws RoomNotFoundException + */ + protected function getRoom(string $objectId): Room { + if (array_key_exists($objectId, $this->rooms)) { + if ($this->rooms[$objectId] === null) { + throw new RoomNotFoundException('Room does not exist'); + } + + return $this->rooms[$objectId]; + } + + try { + $room = $this->manager->getRoomByToken($objectId); + $this->rooms[$objectId] = $room; + return $room; + } catch (RoomNotFoundException $e) { + try { + // Before 3.2.3 the id was passed in notifications + $room = $this->manager->getRoomById((int) $objectId); + $this->rooms[$objectId] = $room; + return $room; + } catch (RoomNotFoundException $e) { + // Room does not exist + $this->rooms[$objectId] = null; + throw $e; + } + } + } + + /** + * @param Room $room + * @param string $userId + * @return Participant + * @throws ParticipantNotFoundException + */ + protected function getParticipant(Room $room, string $userId): Participant { + $roomId = $room->getId(); + if (array_key_exists($roomId, $this->participants) && array_key_exists($userId, $this->participants[$roomId])) { + if ($this->participants[$roomId][$userId] === null) { + throw new ParticipantNotFoundException('Participant does not exist'); + } + + return $this->participants[$roomId][$userId]; + } + + try { + $participant = $room->getParticipant($userId); + $this->participants[$roomId][$userId] = $participant; + return $participant; + } catch (ParticipantNotFoundException $e) { + // Participant does not exist + $this->participants[$roomId][$userId] = null; + throw $e; + } + } + + /** * @param INotification $notification * @param string $languageCode The code of the language that should be used to prepare the notification * @return INotification @@ -138,24 +202,26 @@ class Notifier implements INotifier { $l = $this->lFactory->get('spreed', $languageCode); try { - $room = $this->manager->getRoomByToken($notification->getObjectId()); + $room = $this->getRoom($notification->getObjectId()); } catch (RoomNotFoundException $e) { + // Room does not exist + throw new AlreadyProcessedException(); + } + + if ($this->notificationManager->isPreparingPushNotification() && $notification->getSubject() === 'call') { + // Skip the participant check when we generate push notifications + // we just looped over the participants to create the notification, + // they can not be removed between these 2 steps, but we can save + // n queries. + } else { try { - // Before 3.2.3 the id was passed in notifications - $room = $this->manager->getRoomById((int) $notification->getObjectId()); - } catch (RoomNotFoundException $e) { + $participant = $this->getParticipant($room, $userId); + } catch (ParticipantNotFoundException $e) { // Room does not exist throw new AlreadyProcessedException(); } } - try { - $participant = $room->getParticipant($userId); - } catch (ParticipantNotFoundException $e) { - // Room does not exist - throw new AlreadyProcessedException(); - } - $notification ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('spreed', 'app-dark.svg'))) ->setLink($this->url->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken()])); diff --git a/tests/php/Notification/NotifierTest.php b/tests/php/Notification/NotifierTest.php index 9c74fd66c..c94ce6c0e 100644 --- a/tests/php/Notification/NotifierTest.php +++ b/tests/php/Notification/NotifierTest.php @@ -209,6 +209,125 @@ class NotifierTest extends \Test\TestCase { $this->notifier->prepare($n, 'de'); } + /** + * @dataProvider dataPrepareOne2One + * @param string $uid + * @param string $displayName + * @param string $parsedSubject + */ + public function testPreparingMultipleTimesOnlyGetsTheRoomOnce($uid, $displayName, $parsedSubject) { + $numNotifications = 4; + + $l = $this->createMock(IL10N::class); + $l->expects($this->any()) + ->method('t') + ->will($this->returnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + })); + + $room = $this->createMock(Room::class); + $room->expects($this->any()) + ->method('getType') + ->willReturn(Room::ONE_TO_ONE_CALL); + $room->expects($this->any()) + ->method('getId') + ->willReturn(1234); + $room->expects($this->any()) + ->method('getDisplayName') + ->with('recipient') + ->willReturn($displayName); + $this->manager->expects($this->once()) + ->method('getRoomByToken') + ->with('roomToken') + ->willReturn($room); + + $participant = $this->createMock(Participant::class); + $room->expects($this->once()) + ->method('getParticipant') + ->with('recipient') + ->willReturn($participant); + + $this->lFactory->expects($this->exactly($numNotifications)) + ->method('get') + ->with('spreed', 'de') + ->willReturn($l); + + $recipient = $this->createMock(IUser::class); + $u = $this->createMock(IUser::class); + $u->expects($this->exactly($numNotifications * 2)) + ->method('getDisplayName') + ->willReturn($displayName); + $this->userManager->expects($this->any()) + ->method('get') + ->willReturnMap([ + ['recipient', $recipient], + [$uid, $u], + ]); + + + $n = $this->getNotificationMock($parsedSubject, $uid, $displayName); + $this->notifier->prepare($n, 'de'); + $n = $this->getNotificationMock($parsedSubject, $uid, $displayName); + $this->notifier->prepare($n, 'de'); + $n = $this->getNotificationMock($parsedSubject, $uid, $displayName); + $this->notifier->prepare($n, 'de'); + $n = $this->getNotificationMock($parsedSubject, $uid, $displayName); + $this->notifier->prepare($n, 'de'); + } + + public function getNotificationMock(string $parsedSubject, string $uid, string $displayName) { + /** @var INotification|MockObject $n */ + $n = $this->createMock(INotification::class); + $n->expects($this->once()) + ->method('setIcon') + ->willReturnSelf(); + $n->expects($this->once()) + ->method('setLink') + ->willReturnSelf(); + $n->expects($this->once()) + ->method('setParsedSubject') + ->with($parsedSubject) + ->willReturnSelf(); + $n->expects($this->once()) + ->method('setRichSubject') + ->with('{user} invited you to a private conversation',[ + 'user' => [ + 'type' => 'user', + 'id' => $uid, + 'name' => $displayName, + ], + 'call' => [ + 'type' => 'call', + 'id' => 1234, + 'name' => $displayName, + 'call-type' => 'one2one' + ], + ]) + ->willReturnSelf(); + + + $n->expects($this->exactly(2)) + ->method('getUser') + ->willReturn('recipient'); + $n->expects($this->once()) + ->method('getApp') + ->willReturn('spreed'); + $n->expects($this->once()) + ->method('getSubject') + ->willReturn('invitation'); + $n->expects($this->once()) + ->method('getSubjectParameters') + ->willReturn([$uid]); + $n->expects($this->once()) + ->method('getObjectType') + ->willReturn('room'); + $n->expects($this->once()) + ->method('getObjectId') + ->willReturn('roomToken'); + + return $n; + } + public function dataPrepareGroup() { return [ [Room::GROUP_CALL, 'admin', 'Admin', 'Group', 'Admin invited you to a group conversation: Group'], @@ -278,7 +397,7 @@ class NotifierTest extends \Test\TestCase { ->with($parsedSubject) ->willReturnSelf(); - $room->expects($this->once()) + $room->expects($this->exactly(2)) ->method('getId') ->willReturn($roomId); @@ -786,7 +905,7 @@ class NotifierTest extends \Test\TestCase { ->with('roomToken') ->willReturn($room); } elseif ($validRoom === false) { - $n->expects($this->exactly(2)) + $n->expects($this->once()) ->method('getObjectId') ->willReturn('roomToken'); $this->manager->expects($this->once()) |