Skip to content

Commit c87c0fe

Browse files
committed
Add job to apply ttl
Signed-off-by: Vitor Mattos <vitor@php.rio>
1 parent ed6634d commit c87c0fe

9 files changed

Lines changed: 186 additions & 9 deletions

File tree

appinfo/info.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
6262
<job>OCA\Talk\BackgroundJob\CheckReferenceIdColumn</job>
6363
<job>OCA\Talk\BackgroundJob\CheckHostedSignalingServer</job>
6464
<job>OCA\Talk\BackgroundJob\CheckMatterbridges</job>
65+
<job>OCA\Talk\BackgroundJob\ApplyTtl</job>
6566
</background-jobs>
6667

6768
<repair-steps>

lib/BackgroundJob/ApplyTtl.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
6+
*
7+
* @author Morris Jobke <hey@morrisjobke.de>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*
24+
*/
25+
26+
namespace OCA\Talk\BackgroundJob;
27+
28+
use OCA\Talk\Service\RoomService;
29+
use OCP\AppFramework\Utility\ITimeFactory;
30+
use OCP\BackgroundJob\IJob;
31+
use OCP\BackgroundJob\TimedJob;
32+
33+
class ApplyTtl extends TimedJob {
34+
private RoomService $roomService;
35+
36+
public function __construct(ITimeFactory $timeFactory,
37+
RoomService $roomService) {
38+
parent::__construct($timeFactory);
39+
$this->roomService = $roomService;
40+
41+
// Every 5 minutes
42+
$this->setInterval(5 * 60);
43+
$this->setTimeSensitivity(IJob::TIME_SENSITIVE);
44+
}
45+
46+
/**
47+
* @param array $argument
48+
*/
49+
protected function run($argument): void {
50+
$this->roomService->deleteExpiredTtl($argument['room_id'], $this->getId());
51+
}
52+
}

lib/Service/RoomService.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
namespace OCA\Talk\Service;
2525

26+
use DateInterval;
2627
use InvalidArgumentException;
28+
use OCA\Talk\BackgroundJob\ApplyTtl;
2729
use OCA\Talk\Events\ChangeTtlEvent;
2830
use OCA\Talk\Events\ModifyLobbyEvent;
2931
use OCA\Talk\Events\ModifyRoomEvent;
@@ -34,30 +36,36 @@
3436
use OCA\Talk\Participant;
3537
use OCA\Talk\Room;
3638
use OCA\Talk\Webinary;
39+
use OCP\AppFramework\Utility\ITimeFactory;
40+
use OCP\BackgroundJob\IJobList;
3741
use OCP\DB\QueryBuilder\IQueryBuilder;
3842
use OCP\EventDispatcher\IEventDispatcher;
3943
use OCP\IDBConnection;
4044
use OCP\IUser;
4145
use OCP\Security\IHasher;
46+
use OCP\Server;
4247
use OCP\Share\IManager as IShareManager;
4348

4449
class RoomService {
4550
protected Manager $manager;
4651
protected ParticipantService $participantService;
4752
protected IDBConnection $db;
53+
protected ITimeFactory $timeFactory;
4854
protected IShareManager $shareManager;
4955
protected IHasher $hasher;
5056
protected IEventDispatcher $dispatcher;
5157

5258
public function __construct(Manager $manager,
5359
ParticipantService $participantService,
5460
IDBConnection $db,
61+
ITimeFactory $timeFactory,
5562
IShareManager $shareManager,
5663
IHasher $hasher,
5764
IEventDispatcher $dispatcher) {
5865
$this->manager = $manager;
5966
$this->participantService = $participantService;
6067
$this->db = $db;
68+
$this->timeFactory = $timeFactory;
6169
$this->shareManager = $shareManager;
6270
$this->hasher = $hasher;
6371
$this->dispatcher = $dispatcher;
@@ -396,11 +404,60 @@ public function verifyPassword(Room $room, string $password): array {
396404
public function setTimeToLive(Room $room, int $ttl): void {
397405
$event = new ChangeTtlEvent($room, $ttl);
398406
$this->dispatcher->dispatch(Room::EVENT_BEFORE_SET_TIME_TO_LIVE, $event);
407+
399408
$update = $this->db->getQueryBuilder();
400409
$update->update('talk_rooms')
401410
->set('time_to_live', $update->createNamedParameter($ttl, IQueryBuilder::PARAM_INT))
402411
->where($update->expr()->eq('id', $update->createNamedParameter($room->getId(), IQueryBuilder::PARAM_INT)));
403412
$update->executeStatement();
413+
$jobList = Server::get(IJobList::class);
414+
if ($ttl > 0) {
415+
$jobList->add(ApplyTtl::class, ['room_id' => $room->getId()]);
416+
} else {
417+
$jobList->remove(ApplyTtl::class, ['room_id' => $room->getId()]);
418+
}
419+
404420
$this->dispatcher->dispatch(Room::EVENT_AFTER_SET_TIME_TO_LIVE, $event);
405421
}
422+
423+
public function deleteExpiredTtl(int $roomId, int $jobId): void {
424+
$room = $this->manager->getRoomById($roomId);
425+
426+
$max = $this->getMaxDateTtl($room->getTimeToLive());
427+
$min = $this->getMinDateTtl($jobId);
428+
429+
$this->deleteMessagesByRoomIdInDateInterval($roomId, $min, $max);
430+
}
431+
432+
private function getMaxDateTtl(int $ttl): \DateTime {
433+
$max = $this->timeFactory->getDateTime();
434+
return $max->sub(new DateInterval('PT' . $ttl . 'S'));
435+
}
436+
437+
private function getMinDateTtl(int $jobId): \DateTime {
438+
$query = $this->db->getQueryBuilder();
439+
$query->select('last_checked')
440+
->from('jobs')
441+
->where(
442+
$query->expr()->eq('id', $query->createNamedParameter($jobId, IQueryBuilder::PARAM_INT))
443+
);
444+
$result = $query->executeQuery();
445+
$lastCheckedEpoch = $result->fetchOne();
446+
$lastChechedDateTime = $this->timeFactory->getDateTime('@' . $lastCheckedEpoch);
447+
return $lastChechedDateTime;
448+
}
449+
450+
private function deleteMessagesByRoomIdInDateInterval(int $roomId, \DateTime $min, \DateTime $max): void {
451+
$delete = $this->db->getQueryBuilder();
452+
$delete->delete('comments')
453+
->where(
454+
$delete->expr()->andX(
455+
$delete->expr()->eq('object_id', $delete->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)),
456+
$delete->expr()->eq('object_type', $delete->createNamedParameter('chat')),
457+
$delete->expr()->gte('creation_timestamp', $delete->createNamedParameter($min, IQueryBuilder::PARAM_DATE)),
458+
$delete->expr()->lte('creation_timestamp', $delete->createNamedParameter($max, IQueryBuilder::PARAM_DATE))
459+
)
460+
);
461+
$delete->executeStatement();
462+
}
406463
}

tests/integration/features/bootstrap/FeatureContext.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2331,18 +2331,35 @@ public function userSetTheTtlToWith(string $user, int $ttl, string $identifier,
23312331
/**
23322332
* @Given user :user check if ttl of room :identifier is :ttl (:apiVersion)
23332333
*/
2334-
public function theTtlOfRoomIsV(string $user, string $identifier, int $ttl, string $apiVersion = 'v4') {
2334+
public function userCheckIfTtlOfRoomIsX(string $user, string $identifier, int $ttl, string $apiVersion = 'v4') {
23352335
$this->setCurrentUser($user);
2336-
$this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room');
2337-
$rooms = $this->getDataFromResponse($this->response);
2336+
$this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier]);
2337+
$room = $this->getDataFromResponse($this->response);
23382338

2339-
$rooms = array_filter($rooms, function ($room) {
2340-
return $room['type'] !== 4;
2341-
});
2342-
Assert::assertEquals($ttl, $rooms[0]['timeToLive']);
2339+
Assert::assertEquals($ttl, $room['timeToLive']);
23432340
}
23442341

2342+
/**
2343+
* @When wait for :seconds seconds
2344+
*/
2345+
public function waitForXSeconds($seconds): void {
2346+
sleep($seconds);
2347+
}
23452348

2349+
/**
2350+
* @When apply ttl job to room :identifier
2351+
*/
2352+
public function applyTtlJobToRoom($identifier): void {
2353+
$currentUser = $this->currentUser;
2354+
$this->setCurrentUser('admin');
2355+
$this->sendRequest('GET', '/apps/spreedcheats/get_ttl_job/' . self::$identifierToToken[$identifier]);
2356+
$response = $this->response->getBody()->getContents();
2357+
$response = json_decode($response, true);
2358+
Assert::assertIsArray($response, 'Room ' . $identifier . 'not found');
2359+
Assert::assertArrayHasKey('id', $response);
2360+
$this->runOcc(['background-job:execute', $response['id']]);
2361+
$this->setCurrentUser($currentUser);
2362+
}
23462363

23472364
/*
23482365
* Requests

tests/integration/features/ttl/ttl.feature

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@ Feature: room/ttp
33
Given user "participant1" exists
44
Given user "participant2" exists
55
Given user "participant3" exists
6+
67
Scenario: Enable TTL and check after expire
78
Given user "participant1" creates room "room" (v4)
89
| roomType | 3 |
910
| roomName | room |
1011
And user "participant1" adds user "participant2" to room "room" with 200 (v4)
12+
And user "participant1" sends message "Message 1" to room "room" with 201
1113
And user "participant1" set the ttl to -1 of room "room" with 400 (v4)
1214
And user "participant2" set the ttl to 3 of room "room" with 403 (v4)
1315
And user "participant3" set the ttl to 3 of room "room" with 404 (v4)
1416
And user "participant1" set the ttl to 3 of room "room" with 200 (v4)
15-
And user "participant1" sends message "Message 1" to room "room" with 201
16-
And user "participant1" check if ttl of room "room" is 3 (v4)
17+
And user "participant1" sends message "Message 2" to room "room" with 201
18+
And user "participant1" check if ttl of room "room" is 3 (v4)
19+
And wait for 3 seconds
20+
And apply ttl job to room "room"
21+
Then user "participant1" sees the following messages in room "room" with 200
22+
| room | actorType | actorId | actorDisplayName | message | messageParameters | parentMessage |
23+
| room | users | participant1 | participant1-displayname | Message 1 | [] | |

tests/integration/spreedcheats/appinfo/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@
2626
return [
2727
'ocs' => [
2828
['name' => 'Api#resetSpreed', 'url' => '/', 'verb' => 'DELETE'],
29+
['name' => 'Api#getTtlJob', 'url' => '/get_ttl_job/{token}', 'verb' => 'GET'],
2930
],
3031
];

tests/integration/spreedcheats/lib/Controller/ApiController.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
use OCP\AppFramework\OCSController;
2929
use OCP\AppFramework\Http\DataResponse;
30+
use OCP\AppFramework\Http\JSONResponse;
3031
use OCP\IDBConnection;
3132
use OCP\IRequest;
3233
use OCP\Share\IShare;
@@ -96,4 +97,41 @@ public function resetSpreed(): DataResponse {
9697

9798
return new DataResponse();
9899
}
100+
101+
/**
102+
* @NoCSRFRequired
103+
*
104+
* @return JSONResponse
105+
*/
106+
public function getTtlJob($token): JSONResponse {
107+
$class = 'OCA\Talk\BackgroundJob\ApplyTtl';
108+
$roomId = $this->getRoomIdByToken($token);
109+
if (!$roomId) {
110+
return new JSONResponse();
111+
}
112+
$query = $this->db->getQueryBuilder();
113+
$query->select('id')
114+
->from('jobs')
115+
->where(
116+
$query->expr()->andX(
117+
$query->expr()->eq('class', $query->createNamedParameter($class)),
118+
$query->expr()->eq('argument', $query->createNamedParameter(json_encode(['room_id' => (int) $roomId])))
119+
)
120+
);
121+
$result = $query->executeQuery();
122+
$job = $result->fetchOne();
123+
if ($job) {
124+
return new JSONResponse(['id' => (int) $job]);
125+
}
126+
return new JSONResponse();
127+
}
128+
129+
private function getRoomIdByToken(string $token): ?string {
130+
$query = $this->db->getQueryBuilder();
131+
$query->select('id')
132+
->from('talk_rooms')
133+
->where($query->expr()->eq('token', $query->createNamedParameter($token)));
134+
$result = $query->executeQuery();
135+
return $result->fetchOne();
136+
}
99137
}

tests/php/Service/RoomServiceTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ public function setUp(): void {
6767

6868
$this->manager = $this->createMock(Manager::class);
6969
$this->participantService = $this->createMock(ParticipantService::class);
70+
$this->timeFactory = $this->createMock(ITimeFactory::class);
7071
$this->shareManager = $this->createMock(IShareManager::class);
7172
$this->hasher = $this->createMock(IHasher::class);
7273
$this->dispatcher = $this->createMock(IEventDispatcher::class);
7374
$this->service = new RoomService(
7475
$this->manager,
7576
$this->participantService,
7677
\OC::$server->get(IDBConnection::class),
78+
$this->timeFactory,
7779
$this->shareManager,
7880
$this->hasher,
7981
$this->dispatcher
@@ -334,6 +336,7 @@ public function testVerifyPassword(): void {
334336
$this->manager,
335337
$this->participantService,
336338
\OC::$server->get(IDBConnection::class),
339+
$this->timeFactory,
337340
$this->shareManager,
338341
$this->hasher,
339342
$dispatcher

tests/php/Signaling/BackendNotifierTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ public function setUp(): void {
155155
$this->manager,
156156
$this->participantService,
157157
$dbConnection,
158+
$this->timeFactory,
158159
$this->createMock(IManager::class),
159160
$this->createMock(IHasher::class),
160161
$this->dispatcher

0 commit comments

Comments
 (0)