Browse Source
feat(reminders): Implement reminders for messages not in cache
feat(reminders): Implement reminders for messages not in cache
Signed-off-by: Joas Schilling <coding@schilljs.com>pull/11814/head
No known key found for this signature in database
GPG Key ID: 74434EFE0D2E2205
11 changed files with 323 additions and 42 deletions
-
35lib/Controller/ChatController.php
-
128lib/Service/ProxyCacheMessageService.php
-
7lib/Service/ReminderService.php
-
5lib/Service/RoomFormatter.php
-
36openapi-full.json
-
36openapi.json
-
16src/types/openapi/openapi-full.ts
-
16src/types/openapi/openapi.ts
-
7tests/integration/features/federation/chat.feature
-
75tests/integration/features/federation/reminder.feature
-
4tests/php/Controller/ChatControllerTest.php
@ -0,0 +1,128 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/** |
|||
* @copyright Copyright (c) 2024 Joas Schilling <coding@schilljs.com> |
|||
* |
|||
* @author Joas Schilling <coding@schilljs.com> |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
namespace OCA\Talk\Service; |
|||
|
|||
use OCA\Talk\Exceptions\CannotReachRemoteException; |
|||
use OCA\Talk\Model\Message; |
|||
use OCA\Talk\Model\ProxyCacheMessage; |
|||
use OCA\Talk\Model\ProxyCacheMessageMapper; |
|||
use OCA\Talk\Participant; |
|||
use OCA\Talk\ResponseDefinitions; |
|||
use OCA\Talk\Room; |
|||
use OCP\AppFramework\Db\DoesNotExistException; |
|||
use OCP\AppFramework\Http; |
|||
use OCP\DB\Exception as DBException; |
|||
use Psr\Log\LoggerInterface; |
|||
|
|||
/** |
|||
* @psalm-import-type TalkChatMessageWithParent from ResponseDefinitions |
|||
*/ |
|||
class ProxyCacheMessageService { |
|||
public function __construct( |
|||
protected ProxyCacheMessageMapper $mapper, |
|||
protected LoggerInterface $logger, |
|||
) { |
|||
} |
|||
|
|||
/** |
|||
* @throws DoesNotExistException |
|||
*/ |
|||
public function findByRemote(string $remoteServerUrl, string $remoteToken, int $remoteMessageId): ProxyCacheMessage { |
|||
return $this->mapper->findByRemote($remoteServerUrl, $remoteToken, $remoteMessageId); |
|||
} |
|||
|
|||
/** |
|||
* @throws \InvalidArgumentException |
|||
* @throws CannotReachRemoteException |
|||
*/ |
|||
public function syncRemoteMessage(Room $room, Participant $participant, int $messageId): ProxyCacheMessage { |
|||
if (!$room->isFederatedConversation()) { |
|||
throw new \InvalidArgumentException('room'); |
|||
} |
|||
|
|||
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */ |
|||
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class); |
|||
$ocsResponse = $proxy->getMessageContext($room, $participant, $messageId, 1); |
|||
|
|||
if ($ocsResponse->getStatus() !== Http::STATUS_OK || !isset($ocsResponse->getData()[0])) { |
|||
throw new \InvalidArgumentException('message'); |
|||
} |
|||
|
|||
/** @var TalkChatMessageWithParent $messageData */ |
|||
$messageData = $ocsResponse->getData()[0]; |
|||
|
|||
$proxy = new ProxyCacheMessage(); |
|||
$proxy->setLocalToken($room->getToken()); |
|||
$proxy->setRemoteServerUrl($room->getRemoteServer()); |
|||
$proxy->setRemoteToken($room->getRemoteToken()); |
|||
$proxy->setRemoteMessageId($messageData['id']); |
|||
$proxy->setActorType($messageData['actorType']); |
|||
$proxy->setActorId($messageData['actorId']); |
|||
$proxy->setActorDisplayName($messageData['actorDisplayName']); |
|||
$proxy->setMessageType($messageData['messageType']); |
|||
$proxy->setSystemMessage($messageData['systemMessage']); |
|||
if ($messageData['expirationTimestamp']) { |
|||
$proxy->setExpirationDatetime(new \DateTime('@' . $messageData['expirationTimestamp'])); |
|||
} |
|||
$proxy->setCreationDatetime(new \DateTime('@' . $messageData['timestamp'])); |
|||
$proxy->setMessage($messageData['message']); |
|||
$proxy->setMessageParameters(json_encode($messageData['messageParameters'])); |
|||
|
|||
$metaData = []; |
|||
if (!empty($messageData['lastEditActorType']) && !empty($messageData['lastEditActorId'])) { |
|||
$metaData[Message::METADATA_LAST_EDITED_BY_TYPE] = $messageData['lastEditActorType']; |
|||
$metaData[Message::METADATA_LAST_EDITED_BY_ID] = $messageData['lastEditActorId']; |
|||
} |
|||
if (!empty($messageData['lastEditTimestamp'])) { |
|||
$metaData[Message::METADATA_LAST_EDITED_TIME] = $messageData['lastEditTimestamp']; |
|||
} |
|||
if (!empty($messageData['silent'])) { |
|||
$metaData[Message::METADATA_SILENT] = $messageData['silent']; |
|||
} |
|||
$proxy->setMetaData(json_encode($metaData)); |
|||
|
|||
try { |
|||
$this->mapper->insert($proxy); |
|||
} catch (DBException $e) { |
|||
// DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION happens when
|
|||
// multiple users are in the same conversation. We are therefore
|
|||
// informed multiple times about the same remote message.
|
|||
if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { |
|||
$this->logger->error('Error saving proxy cache message failed: ' . $e->getMessage(), ['exception' => $e]); |
|||
throw $e; |
|||
} |
|||
|
|||
$proxy = $this->mapper->findByRemote( |
|||
$room->getRemoteServer(), |
|||
$room->getRemoteToken(), |
|||
$messageData['id'], |
|||
); |
|||
} |
|||
|
|||
return $proxy; |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
Feature: federation/chat |
|||
Background: |
|||
Given user "participant1" exists |
|||
Given user "participant2" exists |
|||
Given user "participant3" exists |
|||
|
|||
Scenario: Get mention suggestions (translating local users to federated users) |
|||
Given the following "spreed" app config is set |
|||
| federation_enabled | yes | |
|||
Given user "participant1" creates room "room" (v4) |
|||
| roomType | 2 | |
|||
| roomName | room | |
|||
And user "participant1" sends message "Message 1" to room "room" with 201 |
|||
And user "participant1" adds federated_user "participant2" to room "room" with 200 (v4) |
|||
And user "participant2" has the following invitations (v1) |
|||
| remoteServerUrl | remoteToken | state | inviterCloudId | inviterDisplayName | |
|||
| LOCAL | room | 0 | participant1@http://localhost:8080 | participant1-displayname | |
|||
And user "participant2" accepts invite to room "room" of server "LOCAL" with 200 (v1) |
|||
| id | name | type | remoteServer | remoteToken | |
|||
| room | room | 2 | LOCAL | room | |
|||
Then user "participant2" is participant of the following rooms (v4) |
|||
| id | type | |
|||
| room | 2 | |
|||
And user "participant2" joins room "LOCAL::room" with 200 (v4) |
|||
And user "participant2" leaves room "LOCAL::room" with 200 (v4) |
|||
And user "participant2" sends message "Message 2" to room "LOCAL::room" with 201 |
|||
When user "participant1" sets reminder for message "Message 2" in room "room" for time 2133349024 with 201 (v1) |
|||
And user "participant2" sets reminder for message "Message 1" in room "LOCAL::room" for time 1234567 with 201 (v1) |
|||
And user "participant1" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
And user "participant2" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
And force run "OCA\Talk\BackgroundJob\Reminder" background jobs |
|||
Then user "participant1" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
And user "participant2" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
| spreed | reminder | room/Message 1 | Reminder: participant1-displayname in conversation room | |
|||
# Participant1 sets timestamp to past so it should trigger now |
|||
When user "participant1" sets reminder for message "Message 2" in room "room" for time 1234567 with 201 (v1) |
|||
And force run "OCA\Talk\BackgroundJob\Reminder" background jobs |
|||
Then user "participant1" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
| spreed | reminder | room/Message 2 | Reminder: participant2-displayname in conversation room | |
|||
And user "participant2" deletes reminder for message "Message 1" in room "LOCAL::room" with 200 (v1) |
|||
And user "participant2" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
|
|||
Scenario: Deleting reminder before the job is executed never triggers a notification |
|||
Given the following "spreed" app config is set |
|||
| federation_enabled | yes | |
|||
Given user "participant1" creates room "room" (v4) |
|||
| roomType | 2 | |
|||
| roomName | room | |
|||
And user "participant1" sends message "Message 1" to room "room" with 201 |
|||
And user "participant1" adds federated_user "participant2" to room "room" with 200 (v4) |
|||
And user "participant1" adds user "participant3" to room "room" with 200 (v4) |
|||
And user "participant2" has the following invitations (v1) |
|||
| remoteServerUrl | remoteToken | state | inviterCloudId | inviterDisplayName | |
|||
| LOCAL | room | 0 | participant1@http://localhost:8080 | participant1-displayname | |
|||
And user "participant2" accepts invite to room "room" of server "LOCAL" with 200 (v1) |
|||
| id | name | type | remoteServer | remoteToken | |
|||
| room | room | 2 | LOCAL | room | |
|||
Then user "participant2" is participant of the following rooms (v4) |
|||
| id | type | |
|||
| room | 2 | |
|||
And user "participant2" joins room "LOCAL::room" with 200 (v4) |
|||
And user "participant2" leaves room "LOCAL::room" with 200 (v4) |
|||
When user "participant2" sets reminder for message "Message 1" in room "LOCAL::room" for time 1234567 with 201 (v1) |
|||
And user "participant2" deletes reminder for message "Message 1" in room "LOCAL::room" with 200 (v1) |
|||
And user "participant2" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
And force run "OCA\Talk\BackgroundJob\Reminder" background jobs |
|||
And user "participant2" has the following notifications |
|||
| app | object_type | object_id | subject | |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue