diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 401a967c988ca..b40d3356d1a99 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -30,6 +30,8 @@ namespace OC\AppFramework\Bootstrap; use Closure; +use OCP\Calendar\Resource\IBackend as IResourceBackend; +use OCP\Calendar\Room\IBackend as IRoomBackend; use OCP\Talk\ITalkBackend; use RuntimeException; use function array_shift; @@ -70,6 +72,12 @@ class RegistrationContext { /** @var null|ServiceRegistration */ private $talkBackendRegistration = null; + /** @var ServiceRegistration[] */ + private $calendarResourceBackendRegistrations = []; + + /** @var ServiceRegistration[] */ + private $calendarRoomBackendRegistrations = []; + /** @var ServiceFactoryRegistration[] */ private $services = []; @@ -271,6 +279,20 @@ public function registerTalkBackend(string $backend): void { $backend ); } + + public function registerCalendarResourceBackend(string $class): void { + $this->context->registerCalendarResourceBackend( + $this->appId, + $class + ); + } + + public function registerCalendarRoomBackend(string $class): void { + $this->context->registerCalendarRoomBackend( + $this->appId, + $class + ); + } }; } @@ -376,6 +398,20 @@ public function registerTalkBackend(string $appId, string $backend) { $this->talkBackendRegistration = new ServiceRegistration($appId, $backend); } + public function registerCalendarResourceBackend(string $appId, string $class) { + $this->calendarResourceBackendRegistrations[] = new ServiceRegistration( + $appId, + $class, + ); + } + + public function registerCalendarRoomBackend(string $appId, string $class) { + $this->calendarRoomBackendRegistrations[] = new ServiceRegistration( + $appId, + $class, + ); + } + /** * @param App[] $apps */ @@ -635,4 +671,20 @@ public function getProfileLinkActions(): array { public function getTalkBackendRegistration(): ?ServiceRegistration { return $this->talkBackendRegistration; } + + /** + * @return ServiceRegistration[] + * @psalm-return ServiceRegistration[] + */ + public function getCalendarResourceBackendRegistrations(): array { + return $this->calendarResourceBackendRegistrations; + } + + /** + * @return ServiceRegistration[] + * @psalm-return ServiceRegistration[] + */ + public function getCalendarRoomBackendRegistrations(): array { + return $this->calendarRoomBackendRegistrations; + } } diff --git a/lib/private/Calendar/Resource/Manager.php b/lib/private/Calendar/Resource/Manager.php index 935541d9c8751..ebde4ac5eb5d5 100644 --- a/lib/private/Calendar/Resource/Manager.php +++ b/lib/private/Calendar/Resource/Manager.php @@ -1,4 +1,7 @@ * @@ -24,26 +27,30 @@ */ namespace OC\Calendar\Resource; +use OC\AppFramework\Bootstrap\Coordinator; use OCP\Calendar\Resource\IBackend; +use OCP\Calendar\Resource\IManager; use OCP\IServerContainer; -class Manager implements \OCP\Calendar\Resource\IManager { +class Manager implements IManager { + private Coordinator $bootstrapCoordinator; - /** @var IServerContainer */ - private $server; + private IServerContainer $server; - /** @var string[] holds all registered resource backends */ + private bool $bootstrapBackendsLoaded = false; + + /** + * @var string[] holds all registered resource backends + * @psalm-var class-string[] + */ private $backends = []; /** @var IBackend[] holds all backends that have been initialized already */ private $initializedBackends = []; - /** - * Manager constructor. - * - * @param IServerContainer $server - */ - public function __construct(IServerContainer $server) { + public function __construct(Coordinator $bootstrapCoordinator, + IServerContainer $server) { + $this->bootstrapCoordinator = $bootstrapCoordinator; $this->server = $server; } @@ -69,12 +76,30 @@ public function unregisterBackend(string $backendClass) { unset($this->backends[$backendClass], $this->initializedBackends[$backendClass]); } + private function fetchBootstrapBackends(): void { + if ($this->bootstrapBackendsLoaded) { + return; + } + + $context = $this->bootstrapCoordinator->getRegistrationContext(); + if ($context === null) { + // Too soon + return; + } + + foreach ($context->getCalendarResourceBackendRegistrations() as $registration) { + $this->backends[] = $registration->getService(); + } + } + /** * @return IBackend[] * @throws \OCP\AppFramework\QueryException * @since 14.0.0 */ public function getBackends():array { + $this->fetchBootstrapBackends(); + foreach ($this->backends as $backend) { if (isset($this->initializedBackends[$backend])) { continue; diff --git a/lib/private/Calendar/Room/Manager.php b/lib/private/Calendar/Room/Manager.php index 25db5197ef26b..7e6af2823a3ef 100644 --- a/lib/private/Calendar/Room/Manager.php +++ b/lib/private/Calendar/Room/Manager.php @@ -1,4 +1,7 @@ * @@ -24,26 +27,30 @@ */ namespace OC\Calendar\Room; +use OC\AppFramework\Bootstrap\Coordinator; use OCP\Calendar\Room\IBackend; +use OCP\Calendar\Room\IManager; use OCP\IServerContainer; -class Manager implements \OCP\Calendar\Room\IManager { +class Manager implements IManager { + private Coordinator $bootstrapCoordinator; - /** @var IServerContainer */ - private $server; + private IServerContainer $server; - /** @var string[] holds all registered resource backends */ - private $backends = []; - - /** @var IBackend[] holds all backends that have been initialized already */ - private $initializedBackends = []; + private bool $bootstrapBackendsLoaded = false; /** - * Manager constructor. - * - * @param IServerContainer $server + * @var string[] holds all registered resource backends + * @psalm-var class-string[] */ - public function __construct(IServerContainer $server) { + private array $backends = []; + + /** @var IBackend[] holds all backends that have been initialized already */ + private array $initializedBackends = []; + + public function __construct(Coordinator $bootstrapCoordinator, + IServerContainer $server) { + $this->bootstrapCoordinator = $bootstrapCoordinator; $this->server = $server; } @@ -69,17 +76,41 @@ public function unregisterBackend(string $backendClass) { unset($this->backends[$backendClass], $this->initializedBackends[$backendClass]); } + private function fetchBootstrapBackends(): void { + if ($this->bootstrapBackendsLoaded) { + return; + } + + $context = $this->bootstrapCoordinator->getRegistrationContext(); + if ($context === null) { + // Too soon + return; + } + + foreach ($context->getCalendarRoomBackendRegistrations() as $registration) { + $this->backends[] = $registration->getService(); + } + } + /** * @return IBackend[] * @throws \OCP\AppFramework\QueryException * @since 14.0.0 */ public function getBackends():array { + $this->fetchBootstrapBackends(); + foreach ($this->backends as $backend) { if (isset($this->initializedBackends[$backend])) { continue; } + /** + * @todo fetch from the app container + * + * The backend might have services injected that can't be build from the + * server container. + */ $this->initializedBackends[$backend] = $this->server->query($backend); } diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 19b5665f5479e..89583912e0084 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -275,4 +275,24 @@ public function registerProfileLinkAction(string $actionClass): void; * @since 24.0.0 */ public function registerTalkBackend(string $backend): void; + + /** + * Register a resource backend for the DAV server + * + * @param string $actionClass + * @psalm-param class-string<\OCP\Calendar\Resource\IBackend> $actionClass + * @return void + * @since 24.0.0 + */ + public function registerCalendarResourceBackend(string $class): void; + + /** + * Register a room backend for the DAV server + * + * @param string $actionClass + * @psalm-param class-string<\OCP\Calendar\Room\IBackend> $actionClass + * @return void + * @since 24.0.0 + */ + public function registerCalendarRoomBackend(string $class): void; } diff --git a/lib/public/Calendar/Resource/IManager.php b/lib/public/Calendar/Resource/IManager.php index 6910ecb164cbf..268cf8ec633f2 100644 --- a/lib/public/Calendar/Resource/IManager.php +++ b/lib/public/Calendar/Resource/IManager.php @@ -1,4 +1,7 @@ * @@ -23,9 +26,8 @@ namespace OCP\Calendar\Resource; /** - * Interface IManager - * * @since 14.0.0 + * @deprecated 24.0.0 */ interface IManager { @@ -35,6 +37,7 @@ interface IManager { * @param string $backendClass * @return void * @since 14.0.0 + * @deprecated 24.0.0 use \OCP\AppFramework\Bootstrap\IRegistrationContext::registerCalendarResourceBackend */ public function registerBackend(string $backendClass); @@ -44,12 +47,14 @@ public function registerBackend(string $backendClass); * @param string $backendClass * @return void * @since 14.0.0 + * @deprecated 24.0.0 */ public function unregisterBackend(string $backendClass); /** * @return IBackend[] * @since 14.0.0 + * @deprecated 24.0.0 */ public function getBackends():array; @@ -57,6 +62,7 @@ public function getBackends():array; * @param string $backendId * @return IBackend|null * @since 14.0.0 + * @deprecated 24.0.0 */ public function getBackend($backendId); @@ -64,6 +70,7 @@ public function getBackend($backendId); * removes all registered backend instances * @return void * @since 14.0.0 + * @deprecated 24.0.0 */ public function clear(); } diff --git a/lib/public/Calendar/Room/IManager.php b/lib/public/Calendar/Room/IManager.php index 6529ad265b68e..c55f3fd4ae4bf 100644 --- a/lib/public/Calendar/Room/IManager.php +++ b/lib/public/Calendar/Room/IManager.php @@ -1,4 +1,7 @@ * @@ -23,9 +26,8 @@ namespace OCP\Calendar\Room; /** - * Interface IManager - * * @since 14.0.0 + * @deprecated 24.0.0 */ interface IManager { @@ -35,6 +37,7 @@ interface IManager { * @param string $backendClass * @return void * @since 14.0.0 + * @deprecated 24.0.0 use \OC\AppFramework\Bootstrap\::registerCalendarRoomBackend */ public function registerBackend(string $backendClass); @@ -44,12 +47,14 @@ public function registerBackend(string $backendClass); * @param string $backendClass * @return void * @since 14.0.0 + * @deprecated 24.0.0 */ public function unregisterBackend(string $backendClass); /** * @return IBackend[] * @since 14.0.0 + * @deprecated 24.0.0 */ public function getBackends():array; @@ -57,6 +62,7 @@ public function getBackends():array; * @param string $backendId * @return IBackend|null * @since 14.0.0 + * @deprecated 24.0.0 */ public function getBackend($backendId); @@ -64,6 +70,7 @@ public function getBackend($backendId); * removes all registered backend instances * @return void * @since 14.0.0 + * @deprecated 24.0.0 */ public function clear(); } diff --git a/tests/lib/Calendar/Resource/ManagerTest.php b/tests/lib/Calendar/Resource/ManagerTest.php index 30b0e16c948ef..8753f547fb292 100644 --- a/tests/lib/Calendar/Resource/ManagerTest.php +++ b/tests/lib/Calendar/Resource/ManagerTest.php @@ -1,4 +1,7 @@ * @@ -23,101 +26,122 @@ namespace Test\Calendar\Resource; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\AppFramework\Bootstrap\RegistrationContext; +use OC\AppFramework\Bootstrap\ServiceRegistration; use OC\Calendar\Resource\Manager; use OCP\Calendar\Resource\IBackend; use OCP\IServerContainer; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class ManagerTest extends TestCase { - /** @var Manager */ - private $manager; + /** @var Coordinator|MockObject */ + private $coordinator; - /** @var IServerContainer */ + /** @var IServerContainer|MockObject */ private $server; + /** @var Manager */ + private $manager; + protected function setUp(): void { parent::setUp(); + $this->coordinator = $this->createMock(Coordinator::class); $this->server = $this->createMock(IServerContainer::class); - $this->manager = new Manager($this->server); + $this->manager = new Manager( + $this->coordinator, + $this->server, + ); } - public function testRegisterUnregisterBackend() { + public function testRegisterUnregisterBackend(): void { $backend1 = $this->createMock(IBackend::class); $backend1->method('getBackendIdentifier')->willReturn('backend_1'); - $this->server->expects($this->at(0)) - ->method('query') - ->with('calendar_resource_backend1') - ->willReturn($backend1); - $backend2 = $this->createMock(IBackend::class); $backend2->method('getBackendIdentifier')->willReturn('backend_2'); - $this->server->expects($this->at(1)) + $this->server->expects(self::exactly(2)) ->method('query') - ->with('calendar_resource_backend2') - ->willReturn($backend2); + ->willReturnMap([ + ['calendar_resource_backend1', true, $backend1,], + ['calendar_resource_backend2', true, $backend2,], + ]); $this->manager->registerBackend('calendar_resource_backend1'); $this->manager->registerBackend('calendar_resource_backend2'); - $this->assertEquals([ + self::assertEquals([ $backend1, $backend2 ], $this->manager->getBackends()); - $this->manager->unregisterBackend('calendar_resource_backend1'); - - $this->assertEquals([ + self::assertEquals([ $backend2 ], $this->manager->getBackends()); } - public function testGetBackend() { - $backend1 = $this->createMock(IBackend::class); - $backend1->method('getBackendIdentifier')->willReturn('backend_1'); - $this->server->expects($this->at(0)) + public function testGetBackendFromBootstrapRegistration(): void { + $backendClass = '\\OCA\\CalendarResourceFoo\\Backend'; + $backend = $this->createMock(IBackend::class); + $backend->method('getBackendIdentifier')->willReturn('from_bootstrap'); + $context = $this->createMock(RegistrationContext::class); + $this->coordinator->expects(self::once()) + ->method('getRegistrationContext') + ->willReturn($context); + $context->expects(self::once()) + ->method('getCalendarResourceBackendRegistrations') + ->willReturn([ + new ServiceRegistration('calendar_resource_foo', $backendClass) + ]); + $this->server->expects(self::once()) ->method('query') - ->with('calendar_resource_backend1') - ->willReturn($backend1); + ->with($backendClass) + ->willReturn($backend); + + self::assertEquals($backend, $this->manager->getBackend('from_bootstrap')); + } + public function testGetBackend(): void { + $backend1 = $this->createMock(IBackend::class); + $backend1->method('getBackendIdentifier')->willReturn('backend_1'); $backend2 = $this->createMock(IBackend::class); $backend2->method('getBackendIdentifier')->willReturn('backend_2'); - $this->server->expects($this->at(1)) + $this->server->expects(self::exactly(2)) ->method('query') - ->with('calendar_resource_backend2') - ->willReturn($backend2); + ->willReturnMap([ + ['calendar_resource_backend1', true, $backend1,], + ['calendar_resource_backend2', true, $backend2,], + ]); $this->manager->registerBackend('calendar_resource_backend1'); $this->manager->registerBackend('calendar_resource_backend2'); - $this->assertEquals($backend1, $this->manager->getBackend('backend_1')); - $this->assertEquals($backend2, $this->manager->getBackend('backend_2')); + self::assertEquals($backend1, $this->manager->getBackend('backend_1')); + self::assertEquals($backend2, $this->manager->getBackend('backend_2')); } - public function testClear() { + public function testClear(): void { $backend1 = $this->createMock(IBackend::class); $backend1->method('getBackendIdentifier')->willReturn('backend_1'); - $this->server->expects($this->at(0)) - ->method('query') - ->with('calendar_resource_backend1') - ->willReturn($backend1); - $backend2 = $this->createMock(IBackend::class); $backend2->method('getBackendIdentifier')->willReturn('backend_2'); - $this->server->expects($this->at(1)) + $this->server->expects(self::exactly(2)) ->method('query') - ->with('calendar_resource_backend2') - ->willReturn($backend2); + ->willReturnMap([ + ['calendar_resource_backend1', true, $backend1,], + ['calendar_resource_backend2', true, $backend2,], + ]); $this->manager->registerBackend('calendar_resource_backend1'); $this->manager->registerBackend('calendar_resource_backend2'); - $this->assertEquals([ + self::assertEquals([ $backend1, $backend2 ], $this->manager->getBackends()); $this->manager->clear(); - $this->assertEquals([], $this->manager->getBackends()); + self::assertEquals([], $this->manager->getBackends()); } } diff --git a/tests/lib/Calendar/Room/ManagerTest.php b/tests/lib/Calendar/Room/ManagerTest.php index b01d16945ad2e..598415246dcb7 100644 --- a/tests/lib/Calendar/Room/ManagerTest.php +++ b/tests/lib/Calendar/Room/ManagerTest.php @@ -1,4 +1,7 @@ * @@ -23,101 +26,124 @@ namespace Test\Calendar\Room; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\AppFramework\Bootstrap\RegistrationContext; +use OC\AppFramework\Bootstrap\ServiceRegistration; use OC\Calendar\Room\Manager; use OCP\Calendar\Room\IBackend; use OCP\IServerContainer; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class ManagerTest extends TestCase { - /** @var Manager */ - private $manager; + /** @var Coordinator|MockObject */ + private $coordinator; - /** @var IServerContainer */ + /** @var IServerContainer|MockObject */ private $server; + /** @var Manager */ + private $manager; + protected function setUp(): void { parent::setUp(); + $this->coordinator = $this->createMock(Coordinator::class); $this->server = $this->createMock(IServerContainer::class); - $this->manager = new Manager($this->server); + $this->manager = new Manager( + $this->coordinator, + $this->server, + ); } - public function testRegisterUnregisterBackend() { + public function testRegisterUnregisterBackend(): void { $backend1 = $this->createMock(IBackend::class); $backend1->method('getBackendIdentifier')->willReturn('backend_1'); - $this->server->expects($this->at(0)) - ->method('query') - ->with('calendar_room_backend1') - ->willReturn($backend1); - $backend2 = $this->createMock(IBackend::class); $backend2->method('getBackendIdentifier')->willReturn('backend_2'); - $this->server->expects($this->at(1)) + $this->server->expects(self::exactly(2)) ->method('query') - ->with('calendar_room_backend2') - ->willReturn($backend2); + ->willReturnMap([ + ['calendar_room_backend1', true, $backend1,], + ['calendar_room_backend2', true, $backend2,], + ]); $this->manager->registerBackend('calendar_room_backend1'); $this->manager->registerBackend('calendar_room_backend2'); - $this->assertEquals([ + self::assertEquals([ $backend1, $backend2 ], $this->manager->getBackends()); $this->manager->unregisterBackend('calendar_room_backend1'); - $this->assertEquals([ + self::assertEquals([ $backend2 ], $this->manager->getBackends()); } - public function testGetBackend() { - $backend1 = $this->createMock(IBackend::class); - $backend1->method('getBackendIdentifier')->willReturn('backend_1'); - $this->server->expects($this->at(0)) + public function testGetBackendFromBootstrapRegistration(): void { + $backendClass = '\\OCA\\CalendarRoomFoo\\Backend'; + $backend = $this->createMock(IBackend::class); + $backend->method('getBackendIdentifier')->willReturn('from_bootstrap'); + $context = $this->createMock(RegistrationContext::class); + $this->coordinator->expects(self::once()) + ->method('getRegistrationContext') + ->willReturn($context); + $context->expects(self::once()) + ->method('getCalendarRoomBackendRegistrations') + ->willReturn([ + new ServiceRegistration('calendar_room_foo', $backendClass) + ]); + $this->server->expects(self::once()) ->method('query') - ->with('calendar_room_backend1') - ->willReturn($backend1); + ->with($backendClass) + ->willReturn($backend); + + self::assertEquals($backend, $this->manager->getBackend('from_bootstrap')); + } + public function testGetBackend(): void { + $backend1 = $this->createMock(IBackend::class); + $backend1->method('getBackendIdentifier')->willReturn('backend_1'); $backend2 = $this->createMock(IBackend::class); $backend2->method('getBackendIdentifier')->willReturn('backend_2'); - $this->server->expects($this->at(1)) + $this->server->expects(self::exactly(2)) ->method('query') - ->with('calendar_room_backend2') - ->willReturn($backend2); + ->willReturnMap([ + ['calendar_room_backend1', true, $backend1,], + ['calendar_room_backend2', true, $backend2,], + ]); $this->manager->registerBackend('calendar_room_backend1'); $this->manager->registerBackend('calendar_room_backend2'); - $this->assertEquals($backend1, $this->manager->getBackend('backend_1')); - $this->assertEquals($backend2, $this->manager->getBackend('backend_2')); + self::assertEquals($backend1, $this->manager->getBackend('backend_1')); + self::assertEquals($backend2, $this->manager->getBackend('backend_2')); } - public function testClear() { + public function testClear(): void { $backend1 = $this->createMock(IBackend::class); $backend1->method('getBackendIdentifier')->willReturn('backend_1'); - $this->server->expects($this->at(0)) - ->method('query') - ->with('calendar_room_backend1') - ->willReturn($backend1); - $backend2 = $this->createMock(IBackend::class); $backend2->method('getBackendIdentifier')->willReturn('backend_2'); - $this->server->expects($this->at(1)) + $this->server->expects(self::exactly(2)) ->method('query') - ->with('calendar_room_backend2') - ->willReturn($backend2); + ->willReturnMap([ + ['calendar_room_backend1', true, $backend1,], + ['calendar_room_backend2', true, $backend2,], + ]); $this->manager->registerBackend('calendar_room_backend1'); $this->manager->registerBackend('calendar_room_backend2'); - $this->assertEquals([ + self::assertEquals([ $backend1, $backend2 ], $this->manager->getBackends()); $this->manager->clear(); - $this->assertEquals([], $this->manager->getBackends()); + self::assertEquals([], $this->manager->getBackends()); } }