From a8b96fd4f85d951547a21f21b3a6a9e00821bb9e Mon Sep 17 00:00:00 2001 From: provokateurin Date: Mon, 20 Jan 2025 15:05:27 +0100 Subject: [PATCH] feat(UserMountCache): Emit events for added, removed and updated mounts Signed-off-by: provokateurin --- lib/composer/composer/autoload_classmap.php | 3 + lib/composer/composer/autoload_static.php | 3 + lib/private/Files/Config/UserMountCache.php | 46 +++++---- .../Config/Event/UserMountAddedEvent.php | 32 +++++++ .../Config/Event/UserMountRemovedEvent.php | 32 +++++++ .../Config/Event/UserMountUpdatedEvent.php | 33 +++++++ tests/lib/Files/Config/UserMountCacheTest.php | 95 ++++++++++++++----- 7 files changed, 203 insertions(+), 41 deletions(-) create mode 100644 lib/public/Files/Config/Event/UserMountAddedEvent.php create mode 100644 lib/public/Files/Config/Event/UserMountRemovedEvent.php create mode 100644 lib/public/Files/Config/Event/UserMountUpdatedEvent.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index be865f71a306e..7d5db52b14ecc 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -337,6 +337,9 @@ 'OCP\\Files\\Cache\\IScanner' => $baseDir . '/lib/public/Files/Cache/IScanner.php', 'OCP\\Files\\Cache\\IUpdater' => $baseDir . '/lib/public/Files/Cache/IUpdater.php', 'OCP\\Files\\Cache\\IWatcher' => $baseDir . '/lib/public/Files/Cache/IWatcher.php', + 'OCP\\Files\\Config\\Event\\UserMountAddedEvent' => $baseDir . '/lib/public/Files/Config/Event/UserMountAddedEvent.php', + 'OCP\\Files\\Config\\Event\\UserMountRemovedEvent' => $baseDir . '/lib/public/Files/Config/Event/UserMountRemovedEvent.php', + 'OCP\\Files\\Config\\Event\\UserMountUpdatedEvent' => $baseDir . '/lib/public/Files/Config/Event/UserMountUpdatedEvent.php', 'OCP\\Files\\Config\\ICachedMountFileInfo' => $baseDir . '/lib/public/Files/Config/ICachedMountFileInfo.php', 'OCP\\Files\\Config\\ICachedMountInfo' => $baseDir . '/lib/public/Files/Config/ICachedMountInfo.php', 'OCP\\Files\\Config\\IHomeMountProvider' => $baseDir . '/lib/public/Files/Config/IHomeMountProvider.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 19319c3554cd6..5a4e92ac5b1e4 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -370,6 +370,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Files\\Cache\\IScanner' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IScanner.php', 'OCP\\Files\\Cache\\IUpdater' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IUpdater.php', 'OCP\\Files\\Cache\\IWatcher' => __DIR__ . '/../../..' . '/lib/public/Files/Cache/IWatcher.php', + 'OCP\\Files\\Config\\Event\\UserMountAddedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Config/Event/UserMountAddedEvent.php', + 'OCP\\Files\\Config\\Event\\UserMountRemovedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Config/Event/UserMountRemovedEvent.php', + 'OCP\\Files\\Config\\Event\\UserMountUpdatedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Config/Event/UserMountUpdatedEvent.php', 'OCP\\Files\\Config\\ICachedMountFileInfo' => __DIR__ . '/../../..' . '/lib/public/Files/Config/ICachedMountFileInfo.php', 'OCP\\Files\\Config\\ICachedMountInfo' => __DIR__ . '/../../..' . '/lib/public/Files/Config/ICachedMountInfo.php', 'OCP\\Files\\Config\\IHomeMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IHomeMountProvider.php', diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php index ae80d754381ff..4b676092dfef2 100644 --- a/lib/private/Files/Config/UserMountCache.php +++ b/lib/private/Files/Config/UserMountCache.php @@ -11,6 +11,10 @@ use OCP\Cache\CappedMemoryCache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Diagnostics\IEventLogger; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\Event\UserMountAddedEvent; +use OCP\Files\Config\Event\UserMountRemovedEvent; +use OCP\Files\Config\Event\UserMountUpdatedEvent; use OCP\Files\Config\ICachedMountFileInfo; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; @@ -24,9 +28,6 @@ * Cache mounts points per user in the cache so we can easily look them up */ class UserMountCache implements IUserMountCache { - private IDBConnection $connection; - private IUserManager $userManager; - /** * Cached mount info. * @var CappedMemoryCache @@ -37,24 +38,19 @@ class UserMountCache implements IUserMountCache { * @var CappedMemoryCache **/ private CappedMemoryCache $internalPathCache; - private LoggerInterface $logger; /** @var CappedMemoryCache */ private CappedMemoryCache $cacheInfoCache; - private IEventLogger $eventLogger; /** * UserMountCache constructor. */ public function __construct( - IDBConnection $connection, - IUserManager $userManager, - LoggerInterface $logger, - IEventLogger $eventLogger + private IDBConnection $connection, + private IUserManager $userManager, + private LoggerInterface $logger, + private IEventLogger $eventLogger, + private IEventDispatcher $eventDispatcher, ) { - $this->connection = $connection; - $this->userManager = $userManager; - $this->logger = $logger; - $this->eventLogger = $eventLogger; $this->cacheInfoCache = new CappedMemoryCache(); $this->internalPathCache = new CappedMemoryCache(); $this->mountsForUsers = new CappedMemoryCache(); @@ -114,16 +110,28 @@ public function registerMounts(IUser $user, array $mounts, ?array $mountProvider $this->removeFromCache($mount); unset($this->mountsForUsers[$userUID][$mount->getKey()]); } - foreach ($changedMounts as $mount) { - $this->updateCachedMount($mount); + foreach ($changedMounts as $mountPair) { + $newMount = $mountPair[1]; + $this->updateCachedMount($newMount); /** @psalm-suppress InvalidArgument */ - $this->mountsForUsers[$userUID][$mount->getKey()] = $mount; + $this->mountsForUsers[$userUID][$newMount->getKey()] = $newMount; } $this->connection->commit(); } catch (\Throwable $e) { $this->connection->rollBack(); throw $e; } + + // Only fire events after all mounts have already been adjusted in the database. + foreach ($addedMounts as $mount) { + $this->eventDispatcher->dispatchTyped(new UserMountAddedEvent($mount)); + } + foreach ($removedMounts as $mount) { + $this->eventDispatcher->dispatchTyped(new UserMountRemovedEvent($mount)); + } + foreach ($changedMounts as $mountPair) { + $this->eventDispatcher->dispatchTyped(new UserMountUpdatedEvent($mountPair[0], $mountPair[1])); + } } $this->eventLogger->end('fs:setup:user:register'); } @@ -131,9 +139,9 @@ public function registerMounts(IUser $user, array $mounts, ?array $mountProvider /** * @param array $newMounts * @param array $cachedMounts - * @return ICachedMountInfo[] + * @return list Pairs of old and new mounts */ - private function findChangedMounts(array $newMounts, array $cachedMounts) { + private function findChangedMounts(array $newMounts, array $cachedMounts): array { $changed = []; foreach ($cachedMounts as $key => $cachedMount) { if (isset($newMounts[$key])) { @@ -143,7 +151,7 @@ private function findChangedMounts(array $newMounts, array $cachedMounts) { $newMount->getMountId() !== $cachedMount->getMountId() || $newMount->getMountProvider() !== $cachedMount->getMountProvider() ) { - $changed[] = $newMount; + $changed[] = [$cachedMount, $newMount]; } } } diff --git a/lib/public/Files/Config/Event/UserMountAddedEvent.php b/lib/public/Files/Config/Event/UserMountAddedEvent.php new file mode 100644 index 0000000000000..57d8aff72ff04 --- /dev/null +++ b/lib/public/Files/Config/Event/UserMountAddedEvent.php @@ -0,0 +1,32 @@ +fileIds = []; + $this->connection = \OC::$server->getDatabaseConnection(); + $config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); @@ -63,13 +58,22 @@ protected function setUp(): void { ->expects($this->any()) ->method('getAppValue') ->willReturnArgument(2); + $this->userManager = new Manager($config, $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), $this->createMock(LoggerInterface::class)); $userBackend = new Dummy(); $userBackend->createUser('u1', ''); $userBackend->createUser('u2', ''); $userBackend->createUser('u3', ''); $this->userManager->registerBackend($userBackend); - $this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class), $this->createMock(IEventLogger::class)); + + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + + $this->cache = new UserMountCache($this->connection, + $this->userManager, + $this->createMock(LoggerInterface::class), + $this->createMock(IEventLogger::class), + $this->eventDispatcher, + ); } protected function tearDown(): void { @@ -124,7 +128,12 @@ private function keyForMount(MountPoint $mount): string { return $mount->getStorageRootId().'::'.$mount->getMountPoint(); } - public function testNewMounts() { + public function testNewMounts(): void { + $this->eventDispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with($this->callback(fn (UserMountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/asd/')); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -144,7 +153,12 @@ public function testNewMounts() { $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } - public function testSameMounts() { + public function testSameMounts(): void { + $this->eventDispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with($this->callback(fn (UserMountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/asd/')); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -168,7 +182,19 @@ public function testSameMounts() { $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } - public function testRemoveMounts() { + public function testRemoveMounts(): void { + $operation = 0; + $this->eventDispatcher + ->expects($this->exactly(2)) + ->method('dispatchTyped') + ->with($this->callback(function (UserMountAddedEvent|UserMountRemovedEvent $event) use (&$operation) { + return match(++$operation) { + 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/asd/', + 2 => $event instanceof UserMountRemovedEvent && $event->mountPoint->getMountPoint() === '/asd/', + default => false, + }; + })); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -187,7 +213,20 @@ public function testRemoveMounts() { $this->assertCount(0, $cachedMounts); } - public function testChangeMounts() { + public function testChangeMounts(): void { + $operation = 0; + $this->eventDispatcher + ->expects($this->exactly(3)) + ->method('dispatchTyped') + ->with($this->callback(function (UserMountAddedEvent|UserMountRemovedEvent $event) use (&$operation) { + return match(++$operation) { + 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/bar/', + 2 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/', + 3 => $event instanceof UserMountRemovedEvent && $event->mountPoint->getMountPoint() === '/bar/', + default => false, + }; + })); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10); @@ -210,7 +249,19 @@ public function testChangeMounts() { $this->assertEquals('/foo/', $cachedMount->getMountPoint()); } - public function testChangeMountId() { + public function testChangeMountId(): void { + $operation = 0; + $this->eventDispatcher + ->expects($this->exactly(2)) + ->method('dispatchTyped') + ->with($this->callback(function (UserMountAddedEvent|UserMountUpdatedEvent $event) use (&$operation) { + return match(++$operation) { + 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/', + 2 => $event instanceof UserMountUpdatedEvent && $event->oldMountPoint->getMountId() === null && $event->newMountPoint->getMountId() === 1, + default => false, + }; + })); + $user = $this->userManager->get('u1'); [$storage] = $this->getStorage(10);