Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorChristoph Wurst <ChristophWurst@users.noreply.github.com>2022-10-28 09:44:33 +0300
committerGitHub <noreply@github.com>2022-10-28 09:44:33 +0300
commit08e11d7e147163119d9ea2070e6ab986489a9acd (patch)
tree0260ce575426dd572b96242269788565a2e47766 /apps
parent613b2a51e79f6b0f1ef0e2c1fd8580d10b1f252d (diff)
parent6f158733211749ca6d70858ee58ba35e85d46e81 (diff)
Merge pull request #31064 from nextcloud/prune-calendar-changes
Add a background job to prune outdated sync tokens
Diffstat (limited to 'apps')
-rw-r--r--apps/dav/appinfo/info.xml1
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php1
-rw-r--r--apps/dav/composer/composer/autoload_static.php1
-rw-r--r--apps/dav/lib/BackgroundJob/PruneOutdatedSyncTokensJob.php64
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php14
-rw-r--r--apps/dav/lib/CardDAV/CardDavBackend.php14
-rw-r--r--apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php2
-rw-r--r--apps/dav/tests/unit/BackgroundJob/PruneOutdatedSyncTokensJobTest.php104
-rw-r--r--apps/dav/tests/unit/CalDAV/CalDavBackendTest.php54
-rw-r--r--apps/dav/tests/unit/CardDAV/CardDavBackendTest.php18
10 files changed, 272 insertions, 1 deletions
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 134481018a0..dd657564ea9 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -24,6 +24,7 @@
<job>OCA\DAV\BackgroundJob\CleanupInvitationTokenJob</job>
<job>OCA\DAV\BackgroundJob\EventReminderJob</job>
<job>OCA\DAV\BackgroundJob\CalendarRetentionJob</job>
+ <job>OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob</job>
</background-jobs>
<repair-steps>
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index d3290c4e792..ce98cece3a1 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -18,6 +18,7 @@ return array(
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => $baseDir . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
+ 'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => $baseDir . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => $baseDir . '/../lib/BackgroundJob/RefreshWebcalJob.php',
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => $baseDir . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => $baseDir . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 4d425f70f3b..a5a7d34d128 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -33,6 +33,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
+ 'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/RefreshWebcalJob.php',
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => __DIR__ . '/..' . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',
diff --git a/apps/dav/lib/BackgroundJob/PruneOutdatedSyncTokensJob.php b/apps/dav/lib/BackgroundJob/PruneOutdatedSyncTokensJob.php
new file mode 100644
index 00000000000..deca55a26cb
--- /dev/null
+++ b/apps/dav/lib/BackgroundJob/PruneOutdatedSyncTokensJob.php
@@ -0,0 +1,64 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr>
+ *
+ * @author Thomas Citharel <nextcloud@tcit.fr>
+ *
+ * @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\DAV\BackgroundJob;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+use OCA\DAV\AppInfo\Application;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCP\IConfig;
+use Psr\Log\LoggerInterface;
+
+class PruneOutdatedSyncTokensJob extends TimedJob {
+
+ private IConfig $config;
+ private LoggerInterface $logger;
+ private CardDavBackend $cardDavBackend;
+ private CalDavBackend $calDavBackend;
+
+ public function __construct(ITimeFactory $timeFactory, CalDavBackend $calDavBackend, CardDavBackend $cardDavBackend, IConfig $config, LoggerInterface $logger) {
+ parent::__construct($timeFactory);
+ $this->calDavBackend = $calDavBackend;
+ $this->cardDavBackend = $cardDavBackend;
+ $this->config = $config;
+ $this->logger = $logger;
+ $this->setInterval(60 * 60 * 24); // One day
+ $this->setTimeSensitivity(self::TIME_INSENSITIVE);
+ }
+
+ public function run($argument) {
+ $limit = max(1, (int) $this->config->getAppValue(Application::APP_ID, 'totalNumberOfSyncTokensToKeep', '10000'));
+
+ $prunedCalendarSyncTokens = $this->calDavBackend->pruneOutdatedSyncTokens($limit);
+ $prunedAddressBookSyncTokens = $this->cardDavBackend->pruneOutdatedSyncTokens($limit);
+
+ $this->logger->info('Pruned {calendarSyncTokensNumber} calendar sync tokens and {addressBooksSyncTokensNumber} address book sync tokens', [
+ 'calendarSyncTokensNumber' => $prunedCalendarSyncTokens,
+ 'addressBooksSyncTokensNumber' => $prunedAddressBookSyncTokens
+ ]);
+ }
+}
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index 3d5fdb14588..b00a4f9d6ed 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -3105,6 +3105,20 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
/**
+ * @throws \InvalidArgumentException
+ */
+ public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
+ if ($keep < 0) {
+ throw new \InvalidArgumentException();
+ }
+ $query = $this->db->getQueryBuilder();
+ $query->delete('calendarchanges')
+ ->orderBy('id', 'DESC')
+ ->setFirstResult($keep);
+ return $query->executeStatement();
+ }
+
+ /**
* return legacy endpoint principal name to new principal name
*
* @param $principalUri
diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php
index 5d8ee3b9d56..72499ad3037 100644
--- a/apps/dav/lib/CardDAV/CardDavBackend.php
+++ b/apps/dav/lib/CardDAV/CardDavBackend.php
@@ -1327,6 +1327,20 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $this->sharingBackend->applyShareAcl($addressBookId, $acl);
}
+ /**
+ * @throws \InvalidArgumentException
+ */
+ public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
+ if ($keep < 0) {
+ throw new \InvalidArgumentException();
+ }
+ $query = $this->db->getQueryBuilder();
+ $query->delete('addressbookchanges')
+ ->orderBy('id', 'DESC')
+ ->setFirstResult($keep);
+ return $query->executeStatement();
+ }
+
private function convertPrincipal(string $principalUri, bool $toV2): string {
if ($this->principalBackend->getPrincipalPrefix() === 'principals') {
[, $name] = \Sabre\Uri\split($principalUri);
diff --git a/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php b/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php
index 8f1a2a1378f..fd27b4d2776 100644
--- a/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php
+++ b/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php
@@ -44,7 +44,7 @@ class CleanupInvitationTokenJobTest extends TestCase {
/** @var ITimeFactory | \PHPUnit\Framework\MockObject\MockObject */
private $timeFactory;
- /** @var \OCA\DAV\BackgroundJob\GenerateBirthdayCalendarBackgroundJob */
+ /** @var \OCA\DAV\BackgroundJob\CleanupInvitationTokenJob */
private $backgroundJob;
protected function setUp(): void {
diff --git a/apps/dav/tests/unit/BackgroundJob/PruneOutdatedSyncTokensJobTest.php b/apps/dav/tests/unit/BackgroundJob/PruneOutdatedSyncTokensJobTest.php
new file mode 100644
index 00000000000..991caafd597
--- /dev/null
+++ b/apps/dav/tests/unit/BackgroundJob/PruneOutdatedSyncTokensJobTest.php
@@ -0,0 +1,104 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ * @author Joas Schilling <coding@schilljs.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @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\DAV\Tests\unit\BackgroundJob;
+
+use OCA\DAV\AppInfo\Application;
+use OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\DB\Exception;
+use OCP\IConfig;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class PruneOutdatedSyncTokensJobTest extends TestCase {
+
+ /** @var CalDavBackend | MockObject */
+ private $calDavBackend;
+
+ /** @var CardDavBackend | MockObject */
+ private $cardDavBackend;
+
+ /** @var IConfig|MockObject */
+ private $config;
+
+ /** @var LoggerInterface|MockObject*/
+ private $logger;
+
+ /** @var PruneOutdatedSyncTokensJob */
+ private PruneOutdatedSyncTokensJob $backgroundJob;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+ $this->calDavBackend = $this->createMock(CalDavBackend::class);
+ $this->cardDavBackend = $this->createMock(CardDavBackend::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ $this->backgroundJob = new PruneOutdatedSyncTokensJob($this->timeFactory, $this->calDavBackend, $this->cardDavBackend, $this->config, $this->logger);
+ }
+
+ /**
+ * @dataProvider dataForTestRun
+ */
+ public function testRun(string $configValue, int $actualLimit, int $deletedCalendarSyncTokens, int $deletedAddressBookSyncTokens) {
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with(Application::APP_ID, 'totalNumberOfSyncTokensToKeep', '10000')
+ ->willReturn($configValue);
+ $this->calDavBackend->expects($this->once())
+ ->method('pruneOutdatedSyncTokens')
+ ->with($actualLimit)
+ ->willReturn($deletedCalendarSyncTokens);
+ $this->cardDavBackend->expects($this->once())
+ ->method('pruneOutdatedSyncTokens')
+ ->with($actualLimit)
+ ->willReturn($deletedAddressBookSyncTokens);
+ $this->logger->expects($this->once())
+ ->method('info')
+ ->with('Pruned {calendarSyncTokensNumber} calendar sync tokens and {addressBooksSyncTokensNumber} address book sync tokens', [
+ 'calendarSyncTokensNumber' => $deletedCalendarSyncTokens,
+ 'addressBooksSyncTokensNumber' => $deletedAddressBookSyncTokens
+ ]);
+
+ $this->backgroundJob->run(null);
+ }
+
+ public function dataForTestRun(): array {
+ return [
+ ['100', 100, 2, 3],
+ ['0', 1, 0, 0]
+ ];
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
index 7cf04ef5e70..8d5b01996e0 100644
--- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
+++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
@@ -1273,4 +1273,58 @@ EOD;
$this->assertEquals($sharerPrivate, $sharerSearchResults[1]['calendardata']);
$this->assertEquals($sharerConfidential, $sharerSearchResults[2]['calendardata']);
}
+
+ /**
+ * @throws \OCP\DB\Exception
+ * @throws \Sabre\DAV\Exception\BadRequest
+ */
+ public function testPruneOutdatedSyncTokens(): void {
+ $calendarId = $this->createTestCalendar();
+
+ $uri = static::getUniqueID('calobj');
+ $calData = <<<EOD
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Nextcloud Calendar
+BEGIN:VEVENT
+CREATED;VALUE=DATE-TIME:20130910T125139Z
+UID:47d15e3ec8
+LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z
+DTSTAMP;VALUE=DATE-TIME:20130910T125139Z
+SUMMARY:Test Event
+DTSTART;VALUE=DATE-TIME:20130912T130000Z
+DTEND;VALUE=DATE-TIME:20130912T140000Z
+CLASS:PUBLIC
+END:VEVENT
+END:VCALENDAR
+EOD;
+
+ $this->backend->createCalendarObject($calendarId, $uri, $calData);
+
+ // update the card
+ $calData = <<<'EOD'
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:Nextcloud Calendar
+BEGIN:VEVENT
+CREATED;VALUE=DATE-TIME:20130910T125139Z
+UID:47d15e3ec8
+LAST-MODIFIED;VALUE=DATE-TIME:20130910T125139Z
+DTSTAMP;VALUE=DATE-TIME:20130910T125139Z
+SUMMARY:123 Event 🙈
+DTSTART;VALUE=DATE-TIME:20130912T130000Z
+DTEND;VALUE=DATE-TIME:20130912T140000Z
+ATTENDEE;CN=test:mailto:foo@bar.com
+END:VEVENT
+END:VCALENDAR
+EOD;
+ $this->backend->updateCalendarObject($calendarId, $uri, $calData);
+ $deleted = $this->backend->pruneOutdatedSyncTokens(0);
+ // At least one from the object creation and one from the object update
+ $this->assertGreaterThanOrEqual(2, $deleted);
+ $changes = $this->backend->getChangesForCalendar($calendarId, '5', 1);
+ $this->assertEmpty($changes['added']);
+ $this->assertEmpty($changes['modified']);
+ $this->assertEmpty($changes['deleted']);
+ }
}
diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
index 63030910b50..93901b10740 100644
--- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
+++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
@@ -845,4 +845,22 @@ class CardDavBackendTest extends TestCase {
$result = $this->backend->collectCardProperties(666, 'FN');
$this->assertEquals(['John Doe'], $result);
}
+
+ /**
+ * @throws \OCP\DB\Exception
+ * @throws \Sabre\DAV\Exception\BadRequest
+ */
+ public function testPruneOutdatedSyncTokens(): void {
+ $addressBookId = $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
+ $uri = $this->getUniqueID('card');
+ $this->backend->createCard($addressBookId, $uri, $this->vcardTest0);
+ $this->backend->updateCard($addressBookId, $uri, $this->vcardTest1);
+ $deleted = $this->backend->pruneOutdatedSyncTokens(0);
+ // At least one from the object creation and one from the object update
+ $this->assertGreaterThanOrEqual(2, $deleted);
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, '5', 1);
+ $this->assertEmpty($changes['added']);
+ $this->assertEmpty($changes['modified']);
+ $this->assertEmpty($changes['deleted']);
+ }
}