Merge pull request #50990 from AIlkiv/refactor/version-rollback-to-event

refactor: move version rollback hook to event
This commit is contained in:
Stephan Orbaugh 2025-02-27 15:03:37 +01:00 committed by GitHub
commit ef099806a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 158 additions and 12 deletions

View file

@ -17,8 +17,10 @@ return array(
'OCA\\Files_Versions\\Db\\VersionEntity' => $baseDir . '/../lib/Db/VersionEntity.php',
'OCA\\Files_Versions\\Db\\VersionsMapper' => $baseDir . '/../lib/Db/VersionsMapper.php',
'OCA\\Files_Versions\\Events\\CreateVersionEvent' => $baseDir . '/../lib/Events/CreateVersionEvent.php',
'OCA\\Files_Versions\\Events\\VersionRestoredEvent' => $baseDir . '/../lib/Events/VersionRestoredEvent.php',
'OCA\\Files_Versions\\Expiration' => $baseDir . '/../lib/Expiration.php',
'OCA\\Files_Versions\\Listener\\FileEventsListener' => $baseDir . '/../lib/Listener/FileEventsListener.php',
'OCA\\Files_Versions\\Listener\\LegacyRollbackListener' => $baseDir . '/../lib/Listener/LegacyRollbackListener.php',
'OCA\\Files_Versions\\Listener\\LoadAdditionalListener' => $baseDir . '/../lib/Listener/LoadAdditionalListener.php',
'OCA\\Files_Versions\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Versions\\Listener\\VersionAuthorListener' => $baseDir . '/../lib/Listener/VersionAuthorListener.php',

View file

@ -32,8 +32,10 @@ class ComposerStaticInitFiles_Versions
'OCA\\Files_Versions\\Db\\VersionEntity' => __DIR__ . '/..' . '/../lib/Db/VersionEntity.php',
'OCA\\Files_Versions\\Db\\VersionsMapper' => __DIR__ . '/..' . '/../lib/Db/VersionsMapper.php',
'OCA\\Files_Versions\\Events\\CreateVersionEvent' => __DIR__ . '/..' . '/../lib/Events/CreateVersionEvent.php',
'OCA\\Files_Versions\\Events\\VersionRestoredEvent' => __DIR__ . '/..' . '/../lib/Events/VersionRestoredEvent.php',
'OCA\\Files_Versions\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
'OCA\\Files_Versions\\Listener\\FileEventsListener' => __DIR__ . '/..' . '/../lib/Listener/FileEventsListener.php',
'OCA\\Files_Versions\\Listener\\LegacyRollbackListener' => __DIR__ . '/..' . '/../lib/Listener/LegacyRollbackListener.php',
'OCA\\Files_Versions\\Listener\\LoadAdditionalListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalListener.php',
'OCA\\Files_Versions\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Versions\\Listener\\VersionAuthorListener' => __DIR__ . '/..' . '/../lib/Listener/VersionAuthorListener.php',

View file

@ -12,7 +12,9 @@ use OCA\DAV\Connector\Sabre\Principal;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
use OCA\Files_Versions\Capabilities;
use OCA\Files_Versions\Events\VersionRestoredEvent;
use OCA\Files_Versions\Listener\FileEventsListener;
use OCA\Files_Versions\Listener\LegacyRollbackListener;
use OCA\Files_Versions\Listener\LoadAdditionalListener;
use OCA\Files_Versions\Listener\LoadSidebarListener;
use OCA\Files_Versions\Listener\VersionAuthorListener;
@ -80,9 +82,7 @@ class Application extends App implements IBootstrap {
);
});
$context->registerService(IVersionManager::class, function () {
return new VersionManager();
});
$context->registerServiceAlias(IVersionManager::class, VersionManager::class);
/**
* Register Events
@ -108,6 +108,8 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(BeforeNodeCopiedEvent::class, FileEventsListener::class);
$context->registerEventListener(NodeWrittenEvent::class, VersionAuthorListener::class);
$context->registerEventListener(VersionRestoredEvent::class, LegacyRollbackListener::class);
}
public function boot(IBootContext $context): void {

View file

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Versions\Events;
use OCA\Files_Versions\Versions\IVersion;
use OCP\EventDispatcher\Event;
/**
* Class VersionRestoredEvent
*
* Event that is called after a successful restore of a previous version
*
* @package OCA\Files_Versions
*/
class VersionRestoredEvent extends Event {
public function __construct(
private IVersion $version,
) {
}
/**
* Version that was restored
*/
public function getVersion(): IVersion {
return $this->version;
}
}

View file

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Versions\Listener;
use OCA\Files_Versions\Events\VersionRestoredEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
/**
* This listener is designed to be compatible with third-party code
* that can still use a hook. This listener will be removed in
* the next version and the rollback hook will stop working.
*
* @deprecated 32.0.0
* @template-implements IEventListener<VersionRestoredEvent>
*/
class LegacyRollbackListener implements IEventListener {
public function handle(Event $event): void {
if (!($event instanceof VersionRestoredEvent)) {
return;
}
$version = $event->getVersion();
\OC_Hook::emit('\OCP\Versions', 'rollback', [
'path' => $version->getVersionPath(),
'revision' => $version->getRevisionId(),
'node' => $version->getSourceFile(),
]);
}
}

View file

@ -8,6 +8,8 @@ declare(strict_types=1);
*/
namespace OCA\Files_Versions\Versions;
use OCA\Files_Versions\Events\VersionRestoredEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\Files\IRootFolder;
@ -21,9 +23,15 @@ use OCP\Lock\ManuallyLockedException;
use OCP\Server;
class VersionManager implements IVersionManager, IDeletableVersionBackend, INeedSyncVersionBackend, IMetadataVersionBackend {
/** @var (IVersionBackend[])[] */
private $backends = [];
public function __construct(
private IEventDispatcher $dispatcher,
) {
}
public function registerBackend(string $storageType, IVersionBackend $backend) {
if (!isset($this->backends[$storageType])) {
$this->backends[$storageType] = [];
@ -87,11 +95,7 @@ class VersionManager implements IVersionManager, IDeletableVersionBackend, INeed
$result = self::handleAppLocks(fn (): ?bool => $backend->rollback($version));
// rollback doesn't have a return type yet and some implementations don't return anything
if ($result === null || $result === true) {
\OC_Hook::emit('\OCP\Versions', 'rollback', [
'path' => $version->getVersionPath(),
'revision' => $version->getRevisionId(),
'node' => $version->getSourceFile(),
]);
$this->dispatcher->dispatchTyped(new VersionRestoredEvent($version));
}
return $result;
}

View file

@ -9,8 +9,11 @@ declare(strict_types=1);
namespace OCA\files_versions\tests\Versions;
use OC\Files\Storage\Local;
use OCA\Files_Versions\Events\VersionRestoredEvent;
use OCA\Files_Versions\Versions\IVersion;
use OCA\Files_Versions\Versions\IVersionBackend;
use OCA\Files_Versions\Versions\VersionManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Storage\IStorage;
use Test\TestCase;
@ -30,7 +33,8 @@ class VersionManagerTest extends TestCase {
}
public function testGetBackendSingle(): void {
$manager = new VersionManager();
$dispatcher = $this->createMock(IEventDispatcher::class);
$manager = new VersionManager($dispatcher);
$backend = $this->getBackend();
$manager->registerBackend(IStorage::class, $backend);
@ -38,7 +42,8 @@ class VersionManagerTest extends TestCase {
}
public function testGetBackendMoreSpecific(): void {
$manager = new VersionManager();
$dispatcher = $this->createMock(IEventDispatcher::class);
$manager = new VersionManager($dispatcher);
$backend1 = $this->getBackend();
$backend2 = $this->getBackend();
$manager->registerBackend(IStorage::class, $backend1);
@ -48,7 +53,8 @@ class VersionManagerTest extends TestCase {
}
public function testGetBackendNoUse(): void {
$manager = new VersionManager();
$dispatcher = $this->createMock(IEventDispatcher::class);
$manager = new VersionManager($dispatcher);
$backend1 = $this->getBackend();
$backend2 = $this->getBackend(false);
$manager->registerBackend(IStorage::class, $backend1);
@ -58,7 +64,8 @@ class VersionManagerTest extends TestCase {
}
public function testGetBackendMultiple(): void {
$manager = new VersionManager();
$dispatcher = $this->createMock(IEventDispatcher::class);
$manager = new VersionManager($dispatcher);
$backend1 = $this->getBackend();
$backend2 = $this->getBackend(false);
$backend3 = $this->getBackend();
@ -68,4 +75,65 @@ class VersionManagerTest extends TestCase {
$this->assertEquals($backend3, $manager->getBackendForStorage($this->getStorage(Local::class)));
}
public function testRollbackSuccess(): void {
$versionMock = $this->createMock(IVersion::class);
$backendMock = $this->createMock(IVersionBackend::class);
$backendMock->expects($this->once())
->method('rollback')
->with($versionMock)
->willReturn(true);
$versionMock->method('getBackend')->willReturn($backendMock);
$dispatcherMock = $this->createMock(IEventDispatcher::class);
$dispatcherMock->expects($this->once())
->method('dispatchTyped')
->with($this->isInstanceOf(VersionRestoredEvent::class));
$manager = new VersionManager($dispatcherMock);
$this->assertTrue($manager->rollback($versionMock));
}
public function testRollbackNull(): void {
$versionMock = $this->createMock(IVersion::class);
$backendMock = $this->createMock(IVersionBackend::class);
$backendMock->expects($this->once())
->method('rollback')
->with($versionMock)
->willReturn(null);
$versionMock->method('getBackend')->willReturn($backendMock);
$dispatcherMock = $this->createMock(IEventDispatcher::class);
$dispatcherMock->expects($this->once())
->method('dispatchTyped')
->with($this->isInstanceOf(VersionRestoredEvent::class));
$manager = new VersionManager($dispatcherMock);
$this->assertNull($manager->rollback($versionMock));
}
public function testRollbackFailure(): void {
$versionMock = $this->createMock(IVersion::class);
$backendMock = $this->createMock(IVersionBackend::class);
$backendMock->expects($this->once())
->method('rollback')
->with($versionMock)
->willReturn(false);
$versionMock->method('getBackend')->willReturn($backendMock);
$dispatcherMock = $this->createMock(IEventDispatcher::class);
$dispatcherMock->expects($this->never())->method('dispatchTyped');
$manager = new VersionManager($dispatcherMock);
$this->assertFalse($manager->rollback($versionMock));
}
}