diff --git a/CHANGELOG.md b/CHANGELOG.md index 2823c9b..dc3b8b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Latest ------ ## Changes +* [#192](https://github.com/cleverage/ui-process-bundle/issues/192) The controllers and the HTTP value resolver are defined explicitly, without autowiring nor autoconfiguration (Symfony best practices for bundles, as [cleverage/process-bundle#149](https://github.com/cleverage/process-bundle/issues/149)): same arguments, tags and calls. The `$context` binding of the controllers no longer registers the `AdminContextFactory` service for the `AdminContext $context` arguments of the EasyAdmin actions (never used: EasyAdmin resolves the context first). Add tests. * [#187](https://github.com/cleverage/ui-process-bundle/issues/187) Composer: require PHP `^8.2` instead of `>=8.2` (PHP 9 is no longer accepted before being tested), remove the authorization of the `symfony/flex` plugin, which is not required (the CI installs it globally), as [cleverage/process-bundle#319](https://github.com/cleverage/process-bundle/issues/319). * [#181](https://github.com/cleverage/ui-process-bundle/issues/181) Fix the deprecations revealed by the tests, which now fail on deprecations: `ProcessExecutionRepository::getLastProcessExecution()` passes the `\SortDirection` enum to `orderBy()` when doctrine/orm accepts it (3.7 or later, where a string direction is deprecated, [#152](https://github.com/cleverage/ui-process-bundle/issues/152)); `ProcessExecutionRepository::hasLogs()` counts the log records without an arbitrary join (`WITH` deprecated by doctrine/orm 3.7, same result); the date filters of the log records (`createdAt`) and of the process executions (`startDate`) use the `single_text` widget (not configuring it is deprecated by Symfony 6.4, already the default since Symfony 7.0); `User::eraseCredentials()` is marked `#[\Deprecated]`, as required by Symfony 7.3 (with Symfony 6.4 and PHP 8.4 or later, which still call it, set `security.erase_credentials: false` to avoid the deprecation). * [#180](https://github.com/cleverage/ui-process-bundle/issues/180) Define the public API (Symfony BC promise, as [cleverage/process-bundle#279](https://github.com/cleverage/process-bundle/issues/279)): `ProcessExecutionManager`, `ProcessExecutionRepository`, `ProcessScheduleRepository`, `ProcessExecuteMessage`, `ProcessExecutionNotification` and `CleverAgeUiProcessBundle` are marked `@final since 3.3`, they will be `final` in 4.0: extending them triggers a deprecation with the Symfony `DebugClassLoader`. The entities stay open (overriding them: [#37](https://github.com/cleverage/ui-process-bundle/issues/37)). The infrastructure classes are marked `@internal`: the extension, `Configuration`, the commands, the controllers, the EasyAdmin fields and filters, the form types, the Messenger handlers, the Monolog handlers, `HttpProcessExecuteValueResolver`, `HttpProcessExecutionAuthenticator`, `ProcessExecutionStopChecker`, `CronScheduler`, `ProcessEventSubscriber` and the Twig extensions, runtimes and components. diff --git a/config/services/controller.yaml b/config/services/controller.yaml index e735f76..620b7e4 100644 --- a/config/services/controller.yaml +++ b/config/services/controller.yaml @@ -1,20 +1,132 @@ services: - CleverAge\UiProcessBundle\Controller\: - resource: '../../src/Controller/' - autowire: true - autoconfigure: true - bind: + # The controllers extend AbstractController: its services are injected by setContainer(), with the service locator + # of the "container.service_subscriber" tag. The "routing.controller" tag (#[Route] attribute, Symfony 7.3 or + # later) and the "ea.admin_route_controller" tag (#[AdminRoute] attribute) are the ones of the autoconfiguration. + CleverAge\UiProcessBundle\Controller\Admin\LogRecordCrudController: + public: true + arguments: $processConfigurationsManager: '@cleverage_ui_process.manager.process_configuration' - $localeSwitcher: '@translation.locale_switcher' $requestStack: '@request_stack' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - ea.crud_controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\Process\LaunchAction: + public: true + arguments: $messageBus: '@messenger.default_bus' + $requestStack: '@request_stack' + $processConfigurationsManager: '@cleverage_ui_process.manager.process_configuration' + $adminContextProvider: '@EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider' + bind: + # Argument of the action $uploadDirectory: '%upload_directory%' - $context: '@EasyCorp\Bundle\EasyAdminBundle\Factory\AdminContextFactory' - $logDirectory: '%kernel.logs_dir%' - $processExecutionRepository: '@cleverage_ui_process.repository.process_execution' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - routing.controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\Process\ListAction: + public: true + arguments: $intlFormatter: '@EasyCorp\Bundle\EasyAdminBundle\Intl\IntlFormatter' + $processConfigurationsManager: '@cleverage_ui_process.manager.process_configuration' + $adminContextProvider: '@EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - routing.controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\ProcessDashboardController: + public: true + arguments: + $localeSwitcher: '@translation.locale_switcher' + $packages: '@assets.packages' + # $logoPath: set by the extension (design.logo_path) + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - ea.dashboard_controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\ProcessExecutionCrudController: + public: true + arguments: + $processExecutionRepository: '@cleverage_ui_process.repository.process_execution' + $logDirectory: '%kernel.logs_dir%' $translator: '@translator' + # $staleAfter: set by the extension (process_execution.stale_after) + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - ea.admin_route_controller + - ea.crud_controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\ProcessScheduleCrudController: + public: true + arguments: + $processConfigurationsManager: '@cleverage_ui_process.manager.process_configuration' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - ea.crud_controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\Security\LoginController: + public: true + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + # With the service of the argument of the action + - { name: controller.service_arguments, action: __invoke, argument: authenticationUtils, id: security.authentication_utils } + - routing.controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\Security\LogoutController: + public: true + arguments: + $security: '@security.helper' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - routing.controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\Admin\UserCrudController: + public: true + arguments: + # $roles: set by the extension (security.roles) + $adminUrlGenerator: '@EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] + tags: + - controller.service_arguments + - ea.admin_route_controller + - ea.crud_controller + - container.service_subscriber + + CleverAge\UiProcessBundle\Controller\ProcessExecuteController: + public: true + arguments: + $validator: '@validator' + $bus: '@messenger.default_bus' $processManager: '@cleverage_process.manager.process' $processExecutionManager: '@cleverage_ui_process.manager.process_execution' + calls: + - [setContainer, ['@Psr\Container\ContainerInterface']] tags: - - { name: 'controller.service_arguments' } + - controller.service_arguments + - routing.controller + - container.service_subscriber diff --git a/config/services/http_value_resolver.yaml b/config/services/http_value_resolver.yaml index a11c517..30c8b47 100644 --- a/config/services/http_value_resolver.yaml +++ b/config/services/http_value_resolver.yaml @@ -1,8 +1,11 @@ services: cleverage_ui_process.http_value_resolver.http_process_execute: class: CleverAge\UiProcessBundle\Http\ValueResolver\HttpProcessExecuteValueResolver - autoconfigure: true public: false arguments: - '%upload_directory%' - '@serializer' + tags: + # Targeted value resolver: #[ValueResolver('http_process_execution')], as #[AsTargetedValueResolver] + - controller.targeted_value_resolver: { name: http_process_execution } + - controller.argument_value_resolver diff --git a/tests/DependencyInjection/CleverAgeUiProcessExtensionTest.php b/tests/DependencyInjection/CleverAgeUiProcessExtensionTest.php index 15ffcae..51416a5 100644 --- a/tests/DependencyInjection/CleverAgeUiProcessExtensionTest.php +++ b/tests/DependencyInjection/CleverAgeUiProcessExtensionTest.php @@ -71,6 +71,48 @@ public function testEveryServiceDefinitionFileIsLoaded(): void } } + public function testServicesAreDefinedExplicitly(): void + { + $container = $this->createContainer('prod'); + (new CleverAgeUiProcessExtension())->load([], $container); + + // Neither autowiring nor autoconfiguration (Symfony best practices for bundles) + foreach ($container->getDefinitions() as $id => $definition) { + if (!str_starts_with((string) $definition->getClass(), 'CleverAge\\UiProcessBundle\\')) { + continue; + } + self::assertFalse($definition->isAutowired(), \sprintf('Service "%s" is autowired', $id)); + self::assertFalse($definition->isAutoconfigured(), \sprintf('Service "%s" is autoconfigured', $id)); + } + + // The targeted value resolver of the HTTP API (#[AsTargetedValueResolver]) + $resolver = $container->getDefinition('cleverage_ui_process.http_value_resolver.http_process_execute'); + self::assertSame([['name' => 'http_process_execution']], $resolver->getTag('controller.targeted_value_resolver')); + self::assertTrue($resolver->hasTag('controller.argument_value_resolver')); + } + + public function testEveryControllerIsRegistered(): void + { + $container = $this->createContainer('prod'); + (new CleverAgeUiProcessExtension())->load([], $container); + + $files = array_merge(...array_map( + static fn (string $pattern): array => glob($pattern) ?: [], + [\dirname(__DIR__, 2).'/src/Controller/*.php', \dirname(__DIR__, 2).'/src/Controller/*/*.php', \dirname(__DIR__, 2).'/src/Controller/*/*/*.php'] + )); + self::assertNotEmpty($files); + foreach ($files as $file) { + $class = 'CleverAge\\UiProcessBundle\\Controller\\'.str_replace('/', '\\', substr($file, \strlen(\dirname(__DIR__, 2).'/src/Controller/'), -4)); + self::assertTrue($container->hasDefinition($class), \sprintf('Controller "%s" is not registered', $class)); + $definition = $container->getDefinition($class); + self::assertTrue($definition->isPublic()); + self::assertTrue($definition->hasTag('controller.service_arguments')); + // Services of AbstractController + self::assertTrue($definition->hasTag('container.service_subscriber')); + self::assertTrue($definition->hasMethodCall('setContainer')); + } + } + public function testLoadWithDefaultConfiguration(): void { $container = $this->createContainer('prod');