diff --git a/appinfo/info.xml b/appinfo/info.xml index 2ae9761e05a..4de99f98a38 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,7 +11,7 @@ - **๐Ÿ’พ Open format:** Files are saved as [Markdown](https://en.wikipedia.org/wiki/Markdown), so you can edit them from any other text app too. - **โœŠ Strong foundation:** We use [๐Ÿˆ tiptap](https://tiptap.scrumpy.io) which is based on [๐Ÿฆ‰ ProseMirror](https://prosemirror.net) โ€“ huge thanks to them! ]]> - 3.9.0 + 3.9.1 agpl Julius Hรคrtl Text diff --git a/composer/composer/autoload_classmap.php b/composer/composer/autoload_classmap.php index d72c32971b8..a62403c9fc6 100644 --- a/composer/composer/autoload_classmap.php +++ b/composer/composer/autoload_classmap.php @@ -50,6 +50,7 @@ 'OCA\\Text\\Migration\\Version030201Date20201116123153' => $baseDir . '/../lib/Migration/Version030201Date20201116123153.php', 'OCA\\Text\\Migration\\Version030501Date20220202101853' => $baseDir . '/../lib/Migration/Version030501Date20220202101853.php', 'OCA\\Text\\Migration\\Version030701Date20230207131313' => $baseDir . '/../lib/Migration/Version030701Date20230207131313.php', + 'OCA\\Text\\Migration\\Version030901Date20230615085512' => $baseDir . '/../lib/Migration/Version030901Date20230615085512.php', 'OCA\\Text\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php', 'OCA\\Text\\Service\\ApiService' => $baseDir . '/../lib/Service/ApiService.php', 'OCA\\Text\\Service\\AttachmentService' => $baseDir . '/../lib/Service/AttachmentService.php', diff --git a/composer/composer/autoload_static.php b/composer/composer/autoload_static.php index e15e7fa5700..27f9e5986d1 100644 --- a/composer/composer/autoload_static.php +++ b/composer/composer/autoload_static.php @@ -65,6 +65,7 @@ class ComposerStaticInitText 'OCA\\Text\\Migration\\Version030201Date20201116123153' => __DIR__ . '/..' . '/../lib/Migration/Version030201Date20201116123153.php', 'OCA\\Text\\Migration\\Version030501Date20220202101853' => __DIR__ . '/..' . '/../lib/Migration/Version030501Date20220202101853.php', 'OCA\\Text\\Migration\\Version030701Date20230207131313' => __DIR__ . '/..' . '/../lib/Migration/Version030701Date20230207131313.php', + 'OCA\\Text\\Migration\\Version030901Date20230615085512' => __DIR__ . '/..' . '/../lib/Migration/Version030901Date20230615085512.php', 'OCA\\Text\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php', 'OCA\\Text\\Service\\ApiService' => __DIR__ . '/..' . '/../lib/Service/ApiService.php', 'OCA\\Text\\Service\\AttachmentService' => __DIR__ . '/..' . '/../lib/Service/AttachmentService.php', diff --git a/lib/Db/SessionMapper.php b/lib/Db/SessionMapper.php index 8b8eec553ae..957e21d5768 100644 --- a/lib/Db/SessionMapper.php +++ b/lib/Db/SessionMapper.php @@ -99,30 +99,29 @@ public function findAllInactive() { return $this->findEntities($qb); } - public function deleteInactiveWithoutSteps(?int $documentId = null) { - $qb = $this->db->getQueryBuilder(); - $qb->select('session_id') - ->from('text_steps'); + public function deleteInactiveWithoutSteps(?int $documentId = null): int { + $selectSubQuery = $this->db->getQueryBuilder(); + $selectSubQuery->select('s.id') + ->from('text_sessions', 's') + ->leftJoin('s', 'text_steps', 'st', $selectSubQuery->expr()->eq('st.session_id', 's.id')) + ->where($selectSubQuery->expr()->lt('last_contact', $selectSubQuery->createParameter('lastContact'))) + ->andWhere($selectSubQuery->expr()->isNull('st.id')); if ($documentId !== null) { - $qb->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId))); + $selectSubQuery->andWhere($selectSubQuery->expr()->eq('s.document_id', $selectSubQuery->createParameter('documentId'))); } - $result = $qb - ->groupBy('session_id') - ->executeQuery(); - $activeSessions = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); $qb = $this->db->getQueryBuilder(); - $qb->delete($this->getTableName()); - $qb->where($qb->expr()->lt('last_contact', $qb->createNamedParameter(time() - SessionService::SESSION_VALID_TIME))); - if ($documentId !== null) { - $qb->andWhere($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId))); - } - $qb->andWhere($qb->expr()->notIn('id', $qb->createNamedParameter($activeSessions, IQueryBuilder::PARAM_INT_ARRAY))); + $qb->delete($this->getTableName()) + ->where($qb->expr()->in('id', $qb->createFunction($selectSubQuery->getSQL()))); + $qb->setParameters([ + 'lastContact' => time() - SessionService::SESSION_VALID_TIME, + 'documentId' => $documentId, + ]); + return $qb->executeStatement(); } - public function deleteByDocumentId($documentId) { + public function deleteByDocumentId($documentId): int { $qb = $this->db->getQueryBuilder(); $qb->delete($this->getTableName()) ->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId))); diff --git a/lib/Migration/Version030901Date20230615085512.php b/lib/Migration/Version030901Date20230615085512.php new file mode 100644 index 00000000000..778ed43b9ce --- /dev/null +++ b/lib/Migration/Version030901Date20230615085512.php @@ -0,0 +1,32 @@ +getTable('text_steps'); + if (!$table->hasIndex('ts_session')) { + $table->addIndex(['session_id'], 'ts_session'); + return $schema; + } + + return null; + } +} diff --git a/tests/unit/Db/SessionMapperTest.php b/tests/unit/Db/SessionMapperTest.php new file mode 100644 index 00000000000..310b61e957f --- /dev/null +++ b/tests/unit/Db/SessionMapperTest.php @@ -0,0 +1,101 @@ +sessionMapper = \OCP\Server::get(SessionMapper::class); + $this->stepMapper = \OCP\Server::get(StepMapper::class); + + } + + public function testDeleteInactiveWithoutSteps() { + $this->sessionMapper->deleteByDocumentId(1); + $this->sessionMapper->deleteByDocumentId(2); + $this->sessionMapper->insert(Session::fromParams([ + 'userId' => 'admin', + 'documentId' => 1, + 'lastContact' => 1337, + 'token' => uniqid(), + 'color' => '00ff00', + ])); + $this->sessionMapper->insert(Session::fromParams([ + 'userId' => 'admin', + 'documentId' => 2, + 'lastContact' => 1337, + 'token' => uniqid(), + 'color' => '00ff00', + ])); + $this->sessionMapper->deleteInactiveWithoutSteps(1); + self::assertCount(0, $this->sessionMapper->findAll(1)); + self::assertCount(1, $this->sessionMapper->findAll(2)); + $this->sessionMapper->deleteInactiveWithoutSteps(); + self::assertCount(0, $this->sessionMapper->findAll(2)); + } + + public function testDeleteInactiveWithoutStepsKeep() { + $this->stepMapper->deleteAll(1); + $this->sessionMapper->deleteByDocumentId(1); + $this->sessionMapper->deleteByDocumentId(2); + + $s1 = $this->sessionMapper->insert(Session::fromParams([ + 'userId' => 'admin', + 'documentId' => 1, + 'lastContact' => 1337, + 'token' => uniqid(), + 'color' => '00ff00', + ])); + $s2 = $this->sessionMapper->insert(Session::fromParams([ + 'userId' => 'admin', + 'documentId' => 2, + 'lastContact' => 1337, + 'token' => uniqid(), + 'color' => '00ff00', + ])); + $this->stepMapper->insert(Step::fromParams([ + 'sessionId' => $s1->getId(), + 'documentId' => 1, + 'data' => 'YJSDATA', + 'version' => 1, + ])); + self::assertCount(1, $this->sessionMapper->findAll(1)); + + $this->sessionMapper->deleteInactiveWithoutSteps(1); + self::assertCount(1, $this->sessionMapper->findAll(1)); + self::assertCount(1, $this->sessionMapper->findAll(2)); + + $this->sessionMapper->deleteInactiveWithoutSteps(); + self::assertCount(1, $this->sessionMapper->findAll(1)); + self::assertCount(0, $this->sessionMapper->findAll(2)); + } + + public function testDeleteInactiveWithoutStepsMultiple() { + $this->sessionMapper->deleteByDocumentId(1); + + $count = 1010; + for ($i = 0;$i < $count;$i++) { + $this->sessionMapper->insert(Session::fromParams([ + 'userId' => 'admin', + 'documentId' => 1, + 'lastContact' => 1337, + 'token' => uniqid(), + 'color' => '00ff00', + ])); + } + + self::assertCount($count, $this->sessionMapper->findAll(1)); + + $deleted = $this->sessionMapper->deleteInactiveWithoutSteps(); + self::assertEquals($count, $deleted); + + self::assertCount(0, $this->sessionMapper->findAll(1)); + } +}