Merge pull request #50157 from nextcloud/feat/mountmanager/emit-events

add-integration-tests-for-deleting-a-file-copied-from-a-share
Kate 2025-05-19 17:47:31 +07:00 committed by GitHub
commit 06ce2ccebe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 180 additions and 24 deletions

@ -381,6 +381,9 @@ return array(
'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',

@ -422,6 +422,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',

@ -11,6 +11,10 @@ use OC\User\LazyUser;
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;
@ -46,6 +50,7 @@ class UserMountCache implements IUserMountCache {
private IUserManager $userManager,
private LoggerInterface $logger,
private IEventLogger $eventLogger,
private IEventDispatcher $eventDispatcher,
) {
$this->cacheInfoCache = new CappedMemoryCache();
$this->internalPathCache = new CappedMemoryCache();
@ -108,17 +113,29 @@ class UserMountCache implements IUserMountCache {
$this->removeFromCache($mount);
unset($this->mountsForUsers[$userUID][$mount->getKey()]);
}
foreach ($changedMounts as $mount) {
$this->logger->debug("Updating mount '{$mount->getKey()}' for user '$userUID'", ['app' => 'files', 'mount_provider' => $mount->getMountProvider()]);
$this->updateCachedMount($mount);
foreach ($changedMounts as $mountPair) {
$newMount = $mountPair[1];
$this->logger->debug("Updating mount '{$newMount->getKey()}' for user '$userUID'", ['app' => 'files', 'mount_provider' => $newMount->getMountProvider()]);
$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');
}
@ -126,9 +143,9 @@ class UserMountCache implements IUserMountCache {
/**
* @param array<string, ICachedMountInfo> $newMounts
* @param array<string, ICachedMountInfo> $cachedMounts
* @return ICachedMountInfo[]
* @return list<list{0: ICachedMountInfo, 1: ICachedMountInfo}> 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])) {
@ -138,7 +155,7 @@ class UserMountCache implements IUserMountCache {
$newMount->getMountId() !== $cachedMount->getMountId() ||
$newMount->getMountProvider() !== $cachedMount->getMountProvider()
) {
$changed[] = $newMount;
$changed[] = [$cachedMount, $newMount];
}
}
}

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Files\Config\Event;
use OCP\EventDispatcher\Event;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Mount\IMountPoint;
/**
* Event emitted when a user mount was added.
*
* @since 31.0.0
*/
class UserMountAddedEvent extends Event {
public function __construct(
public readonly IMountPoint|ICachedMountInfo $mountPoint,
) {
parent::__construct();
}
}

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Files\Config\Event;
use OCP\EventDispatcher\Event;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Mount\IMountPoint;
/**
* Event emitted when a user mount was removed.
*
* @since 31.0.0
*/
class UserMountRemovedEvent extends Event {
public function __construct(
public readonly IMountPoint|ICachedMountInfo $mountPoint,
) {
parent::__construct();
}
}

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Files\Config\Event;
use OCP\EventDispatcher\Event;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Mount\IMountPoint;
/**
* Event emitted when a user mount was moved.
*
* @since 31.0.0
*/
class UserMountUpdatedEvent extends Event {
public function __construct(
public readonly IMountPoint|ICachedMountInfo $oldMountPoint,
public readonly IMountPoint|ICachedMountInfo $newMountPoint,
) {
parent::__construct();
}
}

@ -10,6 +10,7 @@ namespace Test\Files\Config;
use OC\DB\Exceptions\DbalException;
use OC\DB\QueryBuilder\Literal;
use OC\Files\Cache\Cache;
use OC\Files\Config\UserMountCache;
use OC\Files\Mount\MountPoint;
use OC\Files\Storage\Storage;
use OC\User\Manager;
@ -17,6 +18,9 @@ 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\ICachedMountInfo;
use OCP\ICacheFactory;
use OCP\IConfig;
@ -30,28 +34,19 @@ use Test\Util\User\Dummy;
* @group DB
*/
class UserMountCacheTest extends TestCase {
/**
* @var IDBConnection
*/
private $connection;
/**
* @var IUserManager
*/
private $userManager;
/**
* @var \OC\Files\Config\UserMountCache
*/
private $cache;
private $fileIds = [];
private IDBConnection $connection;
private IUserManager $userManager;
private IEventDispatcher $eventDispatcher;
private UserMountCache $cache;
private array $fileIds = [];
protected function setUp(): void {
parent::setUp();
$this->fileIds = [];
$this->connection = \OC::$server->getDatabaseConnection();
$config = $this->getMockBuilder(IConfig::class)
->disableOriginalConstructor()
->getMock();
@ -63,13 +58,22 @@ class UserMountCacheTest extends TestCase {
->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 {
@ -121,6 +125,11 @@ class UserMountCacheTest extends TestCase {
}
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);
@ -141,6 +150,11 @@ class UserMountCacheTest extends TestCase {
}
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);
@ -165,6 +179,18 @@ class UserMountCacheTest extends TestCase {
}
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);
@ -184,6 +210,19 @@ class UserMountCacheTest extends TestCase {
}
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);
@ -207,6 +246,18 @@ class UserMountCacheTest extends TestCase {
}
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);