Skip to content

Commit

Permalink
feat(MountManager): Emit events when mounts are added, removed and moved
Browse files Browse the repository at this point in the history
Signed-off-by: provokateurin <[email protected]>
  • Loading branch information
provokateurin committed Jan 13, 2025
1 parent 9c717aa commit e3b1f01
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 32 deletions.
3 changes: 3 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@
'OCP\\Files\\Lock\\LockContext' => $baseDir . '/lib/public/Files/Lock/LockContext.php',
'OCP\\Files\\Lock\\NoLockProviderException' => $baseDir . '/lib/public/Files/Lock/NoLockProviderException.php',
'OCP\\Files\\Lock\\OwnerLockedException' => $baseDir . '/lib/public/Files/Lock/OwnerLockedException.php',
'OCP\\Files\\Mount\\Event\\MountAddedEvent' => $baseDir . '/lib/public/Files/Mount/Event/MountAddedEvent.php',
'OCP\\Files\\Mount\\Event\\MountMovedEvent' => $baseDir . '/lib/public/Files/Mount/Event/MountMovedEvent.php',
'OCP\\Files\\Mount\\Event\\MountRemovedEvent' => $baseDir . '/lib/public/Files/Mount/Event/MountRemovedEvent.php',
'OCP\\Files\\Mount\\IMountManager' => $baseDir . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => $baseDir . '/lib/public/Files/Mount/IMountPoint.php',
'OCP\\Files\\Mount\\IMovableMount' => $baseDir . '/lib/public/Files/Mount/IMovableMount.php',
Expand Down
3 changes: 3 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Lock\\LockContext' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/LockContext.php',
'OCP\\Files\\Lock\\NoLockProviderException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/NoLockProviderException.php',
'OCP\\Files\\Lock\\OwnerLockedException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/OwnerLockedException.php',
'OCP\\Files\\Mount\\Event\\MountAddedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/Event/MountAddedEvent.php',
'OCP\\Files\\Mount\\Event\\MountMovedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/Event/MountMovedEvent.php',
'OCP\\Files\\Mount\\Event\\MountRemovedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/Event/MountRemovedEvent.php',
'OCP\\Files\\Mount\\IMountManager' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountPoint.php',
'OCP\\Files\\Mount\\IMovableMount' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMovableMount.php',
Expand Down
52 changes: 31 additions & 21 deletions lib/private/Files/Mount/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
use OC\Files\SetupManager;
use OC\Files\SetupManagerFactory;
use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Mount\Event\MountAddedEvent;
use OCP\Files\Mount\Event\MountMovedEvent;
use OCP\Files\Mount\Event\MountRemovedEvent;
use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotFoundException;
Expand All @@ -26,43 +30,49 @@ class Manager implements IMountManager {
private CappedMemoryCache $inPathCache;
private SetupManager $setupManager;

public function __construct(SetupManagerFactory $setupManagerFactory) {
public function __construct(
SetupManagerFactory $setupManagerFactory,
private IEventDispatcher $eventDispatcher,
) {
$this->pathCache = new CappedMemoryCache();
$this->inPathCache = new CappedMemoryCache();
$this->setupManager = $setupManagerFactory->create($this);
}

/**
* @param IMountPoint $mount
*/
public function addMount(IMountPoint $mount) {
public function addMount(IMountPoint $mount): void {
$this->mounts[$mount->getMountPoint()] = $mount;
$this->pathCache->clear();
$this->inPathCache->clear();

$this->eventDispatcher->dispatchTyped(new MountAddedEvent($mount));
}

/**
* @param string $mountPoint
*/
public function removeMount(string $mountPoint) {
public function removeMount(string $mountPoint): void {
$mountPoint = Filesystem::normalizePath($mountPoint);
if (\strlen($mountPoint) > 1) {
$mountPoint .= '/';
}
unset($this->mounts[$mountPoint]);
$this->pathCache->clear();
$this->inPathCache->clear();

$mount = $this->mounts[$mountPoint] ?? null;
if ($mount !== null) {
unset($this->mounts[$mountPoint]);
$this->pathCache->clear();
$this->inPathCache->clear();

$this->eventDispatcher->dispatchTyped(new MountRemovedEvent($mount));
}
}

/**
* @param string $mountPoint
* @param string $target
*/
public function moveMount(string $mountPoint, string $target) {
$this->mounts[$target] = $this->mounts[$mountPoint];
unset($this->mounts[$mountPoint]);
$this->pathCache->clear();
$this->inPathCache->clear();
public function moveMount(string $mountPoint, string $target): void {
$mount = $this->mounts[$mountPoint] ?? null;
if ($mount !== null) {
$this->mounts[$target] = $mount;
unset($this->mounts[$mountPoint]);
$this->pathCache->clear();
$this->inPathCache->clear();

$this->eventDispatcher->dispatchTyped(new MountMovedEvent($mount, $mountPoint, $target));
}
}

/**
Expand Down
26 changes: 26 additions & 0 deletions lib/public/Files/Mount/Event/MountAddedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCP\Files\Mount\Event;

use OCP\EventDispatcher\Event;
use OCP\Files\Mount\IMountPoint;

/**
* Event emitted when a mount was added.
*
* @since 31.0.0
*/
class MountAddedEvent extends Event {
public function __construct(
public readonly IMountPoint $mountPoint,
) {
parent::__construct();
}
}
28 changes: 28 additions & 0 deletions lib/public/Files/Mount/Event/MountMovedEvent.php
Original file line number Diff line number Diff line change
@@ -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\Mount\Event;

use OCP\EventDispatcher\Event;
use OCP\Files\Mount\IMountPoint;

/**
* Event emitted when a mount was moved.
*
* @since 31.0.0
*/
class MountMovedEvent extends Event {
public function __construct(
public readonly IMountPoint $mountPoint,
public readonly string $oldPath,
public readonly string $newPath,
) {
parent::__construct();
}
}
26 changes: 26 additions & 0 deletions lib/public/Files/Mount/Event/MountRemovedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCP\Files\Mount\Event;

use OCP\EventDispatcher\Event;
use OCP\Files\Mount\IMountPoint;

/**
* Event emitted when a mount was removed.
*
* @since 31.0.0
*/
class MountRemovedEvent extends Event {
public function __construct(
public readonly IMountPoint $mountPoint,
) {
parent::__construct();
}
}
66 changes: 55 additions & 11 deletions tests/lib/Files/Mount/ManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,65 @@

namespace Test\Files\Mount;

use OC\Files\Mount\Manager;
use OC\Files\Mount\MountPoint;
use OC\Files\SetupManagerFactory;
use OC\Files\Storage\Temporary;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Mount\Event\MountAddedEvent;
use OCP\Files\Mount\Event\MountMovedEvent;
use OCP\Files\Mount\Event\MountRemovedEvent;
use Test\TestCase;

class LongId extends Temporary {
public function getId(): string {
return 'long:' . str_repeat('foo', 50) . parent::getId();
}
}

class ManagerTest extends \Test\TestCase {
/**
* @var \OC\Files\Mount\Manager
*/
private $manager;
class ManagerTest extends TestCase {
private IEventDispatcher $eventDispatcher;
private Manager $manager;

protected function setUp(): void {
parent::setUp();
$this->manager = new \OC\Files\Mount\Manager($this->createMock(SetupManagerFactory::class));

$this->eventDispatcher = $this->createMock(IEventDispatcher::class);

$this->manager = new Manager(
$this->createMock(SetupManagerFactory::class),
$this->eventDispatcher,
);
}

public function testFind(): void {
$rootMount = new \OC\Files\Mount\MountPoint(new Temporary([]), '/');
$rootMount = new MountPoint(new Temporary([]), '/');
$this->manager->addMount($rootMount);
$this->assertEquals($rootMount, $this->manager->find('/'));
$this->assertEquals($rootMount, $this->manager->find('/foo/bar'));

$storage = new Temporary([]);
$mount1 = new \OC\Files\Mount\MountPoint($storage, '/foo');
$mount1 = new MountPoint($storage, '/foo');
$this->manager->addMount($mount1);
$this->assertEquals($rootMount, $this->manager->find('/'));
$this->assertEquals($mount1, $this->manager->find('/foo/bar'));

$this->assertEquals(1, count($this->manager->findIn('/')));
$mount2 = new \OC\Files\Mount\MountPoint(new Temporary([]), '/bar');
$mount2 = new MountPoint(new Temporary([]), '/bar');
$this->manager->addMount($mount2);
$this->assertEquals(2, count($this->manager->findIn('/')));

$id = $mount1->getStorageId();
$this->assertEquals([$mount1], $this->manager->findByStorageId($id));

$mount3 = new \OC\Files\Mount\MountPoint($storage, '/foo/bar');
$mount3 = new MountPoint($storage, '/foo/bar');
$this->manager->addMount($mount3);
$this->assertEquals([$mount1, $mount3], $this->manager->findByStorageId($id));
}

public function testLong(): void {
$storage = new LongId([]);
$mount = new \OC\Files\Mount\MountPoint($storage, '/foo');
$mount = new MountPoint($storage, '/foo');
$this->manager->addMount($mount);

$id = $mount->getStorageId();
Expand All @@ -63,4 +74,37 @@ public function testLong(): void {
$this->assertEquals([$mount], $this->manager->findByStorageId($storageId));
$this->assertEquals([$mount], $this->manager->findByStorageId(md5($storageId)));
}

public function testAddMountEvent(): void {
$this->eventDispatcher
->expects($this->once())
->method('dispatchTyped')
->with($this->callback(fn (MountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/foo/'));

$this->manager->addMount(new MountPoint(new Temporary([]), '/foo'));
}

public function testRemoveMountEvent(): void {
$this->eventDispatcher
->expects($this->exactly(2))
->method('dispatchTyped')
->with($this->callback(fn (MountAddedEvent|MountRemovedEvent $event) => $event->mountPoint->getMountPoint() === '/foo/'));

$this->manager->addMount(new MountPoint(new Temporary([]), '/foo'));
$this->manager->removeMount('/foo');
}

public function testMoveMountEvent(): void {
$this->eventDispatcher
->expects($this->exactly(2))
->method('dispatchTyped')
->with($this->callback(fn (MountAddedEvent|MountMovedEvent $event) =>
($event instanceof MountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/')
// The getMountPoint() still returns the old path in this test because it is updated outside the MountManager before calling moveMount().
|| ($event instanceof MountMovedEvent && $event->mountPoint->getMountPoint() === '/foo/' && $event->oldPath === '/foo/' && $event->newPath === '/bar/'))
);

$this->manager->addMount(new MountPoint(new Temporary([]), '/foo'));
$this->manager->moveMount('/foo/', '/bar/');
}
}

0 comments on commit e3b1f01

Please sign in to comment.