diff --git a/config/doctrine/LogRecord.orm.xml b/config/doctrine/LogRecord.orm.xml new file mode 100644 index 0000000..6e24eef --- /dev/null +++ b/config/doctrine/LogRecord.orm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/config/doctrine/ProcessExecution.orm.xml b/config/doctrine/ProcessExecution.orm.xml new file mode 100644 index 0000000..829d703 --- /dev/null +++ b/config/doctrine/ProcessExecution.orm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/config/doctrine/ProcessSchedule.orm.xml b/config/doctrine/ProcessSchedule.orm.xml new file mode 100644 index 0000000..ae78b65 --- /dev/null +++ b/config/doctrine/ProcessSchedule.orm.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/config/doctrine/User.orm.xml b/config/doctrine/User.orm.xml new file mode 100644 index 0000000..7d12574 --- /dev/null +++ b/config/doctrine/User.orm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/config/services/command.yaml b/config/services/command.yaml index e8f5c9f..5fa8eb4 100644 --- a/config/services/command.yaml +++ b/config/services/command.yaml @@ -5,6 +5,7 @@ services: tags: - { name: console.command } arguments: + - '%cleverage_ui_process.entity.user.class%' - '@validator' - '@security.user_password_hasher' - '@doctrine.orm.entity_manager' diff --git a/config/services/event_subscriber.yaml b/config/services/event_subscriber.yaml index 1a35949..f1205a0 100644 --- a/config/services/event_subscriber.yaml +++ b/config/services/event_subscriber.yaml @@ -5,6 +5,7 @@ services: tags: - { name: 'kernel.event_subscriber' } arguments: + - '%cleverage_ui_process.entity.process_execution.class%' - '@cleverage_ui_process.monolog_handler.process' - '@cleverage_ui_process.monolog_handler.doctrine_process' - '@cleverage_ui_process.manager.process_execution' diff --git a/config/services/monolog_handler.yaml b/config/services/monolog_handler.yaml index 1bd99b8..c506f49 100644 --- a/config/services/monolog_handler.yaml +++ b/config/services/monolog_handler.yaml @@ -5,6 +5,7 @@ services: calls: - [ setEntityManager, [ '@doctrine.orm.entity_manager' ] ] - [ setProcessExecutionManager, [ '@cleverage_ui_process.manager.process_execution' ] ] + - [ setLogRecordClassName, [ '%cleverage_ui_process.entity.log_record.class%' ] ] CleverAge\UiProcessBundle\Monolog\Handler\DoctrineProcessHandler: alias: cleverage_ui_process.monolog_handler.doctrine_process diff --git a/config/services/repository.yaml b/config/services/repository.yaml index 6e8a5bd..f11bf14 100644 --- a/config/services/repository.yaml +++ b/config/services/repository.yaml @@ -4,10 +4,19 @@ services: public: false arguments: - '@doctrine.orm.entity_manager' + - '%cleverage_ui_process.entity.process_execution.class%' + - '%cleverage_ui_process.entity.log_record.class%' cleverage_ui_process.repository.process_schedule: class: CleverAge\UiProcessBundle\Repository\ProcessScheduleRepository public: false arguments: - - '@doctrine' + - '@doctrine.orm.entity_manager' + - '%cleverage_ui_process.entity.process_schedule.class%' + cleverage_ui_process.repository.user: + class: CleverAge\UiProcessBundle\Repository\UserRepository + public: false + arguments: + - '@doctrine.orm.entity_manager' + - '%cleverage_ui_process.entity.user.class%' diff --git a/config/services/security.yaml b/config/services/security.yaml index 2eda664..cd2ccbf 100644 --- a/config/services/security.yaml +++ b/config/services/security.yaml @@ -3,5 +3,5 @@ services: class: CleverAge\UiProcessBundle\Security\HttpProcessExecutionAuthenticator public: false arguments: - - '@doctrine.orm.entity_manager' + - '@cleverage_ui_process.repository.user' diff --git a/config/validation/ProcessSchedule.orm.xml b/config/validation/ProcessSchedule.orm.xml new file mode 100644 index 0000000..faaf797 --- /dev/null +++ b/config/validation/ProcessSchedule.orm.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/index.md b/docs/index.md index db90d03..1b7d8e3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,6 +19,82 @@ Remember to add the following line to `config/bundles.php` (not required if Symf CleverAge\UiProcessBundle\CleverAgeUiProcessBundle::class => ['all' => true], ``` +## Doctrine ORM Configuration + +Add these in the config mapping definition (or enable [auto_mapping](https://symfony.com/doc/current/reference/configuration/doctrine.html#mapping-configuration)): + +```yaml +# config/packages/doctrine.yaml + +doctrine: + orm: + mappings: + CleverAgeUiProcessBundle: ~ +``` + +And then create the corresponding entities: + +```php +// src/Entity/LogRecord.php + +use Doctrine\ORM\Mapping as ORM; +use CleverAge\UiProcessBundle\Entity\LogRecord as BaseLogRecord; + +#[ORM\Entity] +#[ORM\Table] +class LogRecord extends BaseLogRecord +{ +} +``` + +```php +// src/Entity/ProcessExecution.php + +use Doctrine\ORM\Mapping as ORM; +use CleverAge\UiProcessBundle\Entity\ProcessExecution as BaseProcessExecution; + +#[ORM\Entity] +#[ORM\Table] +class ProcessExecution extends BaseProcessExecution +{ +} +``` + +```php +// src/Entity/ProcessSchedule.php + +use Doctrine\ORM\Mapping as ORM; +use CleverAge\UiProcessBundle\Entity\ProcessSchedule as BaseProcessSchedule; + +#[ORM\Entity] +#[ORM\Table] +class ProcessSchedule extends BaseProcessSchedule +{ +} +``` + +```php +// src/Entity/User.php + +use Doctrine\ORM\Mapping as ORM; +use CleverAge\UiProcessBundle\Entity\User as BaseUser; + +#[ORM\Entity] +#[ORM\Table(name: 'process_user')] +class User extends BaseUser +{ +} +``` + +So, update your schema: + +```bash +bin/console doctrine:schema:update --force +``` +or use [DoctrineMigrationsBundle](https://github.com/doctrine/DoctrineMigrationsBundle) + +And create a User using `cleverage:ui-process:user-create` console. + ## Import routes ```yaml @@ -26,8 +102,6 @@ ui-process-bundle: resource: '@CleverAgeUiProcessBundle/src/Controller' type: attribute ``` -* Run doctrine migration -* Create a user using `cleverage:ui-process:user-create` console. Now you can access UI Process via http://your-domain.com/process diff --git a/src/CleverAgeUiProcessBundle.php b/src/CleverAgeUiProcessBundle.php index 8acf1d7..d507312 100644 --- a/src/CleverAgeUiProcessBundle.php +++ b/src/CleverAgeUiProcessBundle.php @@ -13,10 +13,20 @@ namespace CleverAge\UiProcessBundle; +use CleverAge\UiProcessBundle\DependencyInjection\Compiler\ResolveTargetEntityPass; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class CleverAgeUiProcessBundle extends Bundle { + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new ResolveTargetEntityPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1000); + } + public function getPath(): string { return \dirname(__DIR__); diff --git a/src/Command/UserCreateCommand.php b/src/Command/UserCreateCommand.php index 806cc0c..c73f056 100644 --- a/src/Command/UserCreateCommand.php +++ b/src/Command/UserCreateCommand.php @@ -13,7 +13,7 @@ namespace CleverAge\UiProcessBundle\Command; -use CleverAge\UiProcessBundle\Entity\User; +use CleverAge\UiProcessBundle\Entity\UserInterface; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -35,7 +35,11 @@ )] class UserCreateCommand extends Command { + /** + * @param class-string $userClassName + */ public function __construct( + private readonly string $userClassName, private readonly ValidatorInterface $validator, private readonly UserPasswordHasherInterface $passwordEncoder, private readonly EntityManagerInterface $em, @@ -54,7 +58,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output ); - $user = new User(); + /** @var UserInterface $user */ + $user = new $this->userClassName(); $user->setEmail($username); $user->setRoles(['ROLE_USER', 'ROLE_ADMIN']); $user->setPassword($this->passwordEncoder->hashPassword($user, $password)); diff --git a/src/Controller/Admin/Process/LaunchAction.php b/src/Controller/Admin/Process/LaunchAction.php index 8f6bed5..340a508 100644 --- a/src/Controller/Admin/Process/LaunchAction.php +++ b/src/Controller/Admin/Process/LaunchAction.php @@ -14,7 +14,7 @@ namespace CleverAge\UiProcessBundle\Controller\Admin\Process; use CleverAge\ProcessBundle\Exception\MissingProcessException; -use CleverAge\UiProcessBundle\Entity\User; +use CleverAge\UiProcessBundle\Entity\UserInterface; use CleverAge\UiProcessBundle\Form\Type\LaunchType; use CleverAge\UiProcessBundle\Manager\ProcessConfigurationsManager; use CleverAge\UiProcessBundle\Message\ProcessExecuteMessage; @@ -130,9 +130,9 @@ protected function dispatch(string $processCode, mixed $input = null, array $con $this->messageBus->dispatch($message); } - protected function getUser(): ?User + protected function getUser(): ?UserInterface { - /** @var User $user */ + /** @var UserInterface $user */ $user = parent::getUser(); return $user; diff --git a/src/Controller/Admin/ProcessExecutionCrudController.php b/src/Controller/Admin/ProcessExecutionCrudController.php index 5e60a60..8071bfb 100644 --- a/src/Controller/Admin/ProcessExecutionCrudController.php +++ b/src/Controller/Admin/ProcessExecutionCrudController.php @@ -16,7 +16,7 @@ use CleverAge\UiProcessBundle\Admin\Field\ContextField; use CleverAge\UiProcessBundle\Admin\Field\EnumField; use CleverAge\UiProcessBundle\Entity\ProcessExecution; -use CleverAge\UiProcessBundle\Repository\ProcessExecutionRepository; +use CleverAge\UiProcessBundle\Repository\ProcessExecutionRepositoryInterface; use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; @@ -36,7 +36,7 @@ class ProcessExecutionCrudController extends AbstractCrudController { public function __construct( - private readonly ProcessExecutionRepository $processExecutionRepository, + private readonly ProcessExecutionRepositoryInterface $processExecutionRepository, private readonly string $logDirectory, ) { } @@ -123,9 +123,8 @@ public function showLogs(AdminContext $adminContext): RedirectResponse return $this->redirect($url); } - public function downloadLogFile( - AdminContext $context, - ): Response { + public function downloadLogFile(AdminContext $context): Response + { /** @var ProcessExecution $processExecution */ $processExecution = $context->getEntity()->getInstance(); $filepath = $this->getLogFilePath($processExecution); @@ -149,7 +148,7 @@ public function configureFilters(Filters $filters): Filters private function getLogFilePath(ProcessExecution $processExecution): string { return $this->logDirectory. - \DIRECTORY_SEPARATOR.$processExecution->code. + \DIRECTORY_SEPARATOR.$processExecution->getCode(). \DIRECTORY_SEPARATOR.$processExecution->logFilename ; } diff --git a/src/DependencyInjection/CleverAgeUiProcessExtension.php b/src/DependencyInjection/CleverAgeUiProcessExtension.php index 2b7b302..fb0011e 100644 --- a/src/DependencyInjection/CleverAgeUiProcessExtension.php +++ b/src/DependencyInjection/CleverAgeUiProcessExtension.php @@ -33,12 +33,18 @@ public function load(array $configs, ContainerBuilder $container): void $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); + $container->getDefinition(UserCrudController::class) ->setArgument('$roles', array_combine($config['security']['roles'], $config['security']['roles'])); $container->getDefinition('cleverage_ui_process.monolog_handler.process') ->addMethodCall('setReportIncrementLevel', [$config['logs']['report_increment_level']]); $container->getDefinition(ProcessDashboardController::class) ->setArgument('$logoPath', $config['design']['logo_path']); + + $container->setParameter('cleverage_ui_process.entity.log_record.class', $config['class']['log_record']); + $container->setParameter('cleverage_ui_process.entity.process_execution.class', $config['class']['process_execution']); + $container->setParameter('cleverage_ui_process.entity.process_schedule.class', $config['class']['process_schedule']); + $container->setParameter('cleverage_ui_process.entity.user.class', $config['class']['user']); } /** diff --git a/src/DependencyInjection/Compiler/ResolveTargetEntityPass.php b/src/DependencyInjection/Compiler/ResolveTargetEntityPass.php new file mode 100644 index 0000000..c8ff1ae --- /dev/null +++ b/src/DependencyInjection/Compiler/ResolveTargetEntityPass.php @@ -0,0 +1,32 @@ +findDefinition('doctrine.orm.listeners.resolve_target_entity') + ->addMethodCall( + 'addResolveTargetEntity', + [ProcessExecutionInterface::class, $container->getParameter('cleverage_ui_process.entity.process_execution.class'), []], + ) + ->addTag('doctrine.event_listener', ['event' => Events::loadClassMetadata]) + ->addTag('doctrine.event_listener', ['event' => Events::onClassMetadataNotFound]) + ; + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 4dd50a6..4b15261 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -25,12 +25,23 @@ public function getConfigTreeBuilder(): TreeBuilder $tb = new TreeBuilder('clever_age_ui_process'); /** @var ArrayNodeDefinition $rootNode */ $rootNode = $tb->getRootNode(); + $rootNode + ->children() + ->arrayNode('class') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('log_record')->defaultValue('App\Entity\LogRecord')->end() + ->scalarNode('process_execution')->defaultValue('App\Entity\ProcessExecution')->end() + ->scalarNode('process_schedule')->defaultValue('App\Entity\ProcessSchedule')->end() + ->scalarNode('user')->defaultValue('App\Entity\User')->end() + ->end(); $rootNode ->children() ->arrayNode('security') ->addDefaultsIfNotSet() ->children() - ->arrayNode('roles')->defaultValue(['ROLE_ADMIN'])->scalarPrototype()->end(); // Roles displayed inside user edit form + ->arrayNode('roles')->defaultValue(['ROLE_ADMIN'])->scalarPrototype() // Roles displayed inside user edit form + ->end(); $rootNode ->children() ->arrayNode('logs') diff --git a/src/Entity/LogRecord.php b/src/Entity/LogRecord.php index 2a9c25f..2a46c84 100644 --- a/src/Entity/LogRecord.php +++ b/src/Entity/LogRecord.php @@ -13,47 +13,25 @@ namespace CleverAge\UiProcessBundle\Entity; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Mapping as ORM; use Symfony\Component\String\UnicodeString; -#[ORM\Entity] -#[ORM\Index(name: 'idx_log_record_level', columns: ['level'])] -#[ORM\Index(name: 'idx_log_record_created_at', columns: ['created_at'])] -class LogRecord +class LogRecord implements LogRecordInterface { - #[ORM\Id] - #[ORM\GeneratedValue] - #[ORM\Column] - private ?int $id = null; + protected ?int $id = null; - #[ORM\Column(type: Types::STRING, length: 64)] public readonly string $channel; - #[ORM\Column(type: Types::INTEGER)] public readonly int $level; - #[ORM\Column(type: Types::STRING, length: 512)] public readonly string $message; - /** @var array $context */ - #[ORM\Column(type: Types::JSON)] + /** @var array */ public readonly array $context; - #[ORM\Column(type: Types::DATETIME_IMMUTABLE)] public readonly \DateTimeImmutable $createdAt; - public function getId(): ?int + public function __construct(\Monolog\LogRecord $record, protected readonly ProcessExecutionInterface $processExecution) { - return $this->id; - } - - public function __construct( - \Monolog\LogRecord $record, - #[ORM\ManyToOne(targetEntity: ProcessExecution::class, cascade: ['all'])] - #[ORM\JoinColumn(name: 'process_execution_id', referencedColumnName: 'id', onDelete: 'CASCADE', nullable: false)] - private readonly ProcessExecution $processExecution, - ) { $this->channel = (string) (new UnicodeString($record->channel))->truncate(64); $this->level = $record->level->value; $this->message = (string) (new UnicodeString($record->message))->truncate(512); @@ -61,6 +39,11 @@ public function __construct( $this->createdAt = \DateTimeImmutable::createFromMutable(new \DateTime()); } + public function getId(): ?int + { + return $this->id; + } + public function contextIsEmpty(): bool { return [] !== $this->context; diff --git a/src/Entity/LogRecordInterface.php b/src/Entity/LogRecordInterface.php new file mode 100644 index 0000000..e0954a7 --- /dev/null +++ b/src/Entity/LogRecordInterface.php @@ -0,0 +1,19 @@ + */ - #[ORM\Column(type: Types::JSON)] - private array $report = []; + protected array $report = []; /** * @var array */ - #[ORM\Column(type: Types::JSON, nullable: true)] - private ?array $context = []; - - public function getId(): ?int - { - return $this->id; - } + protected array $context; /** - * @param array $context + * @param ?array $context */ - public function __construct( - string $code, - #[ORM\Column(type: Types::STRING, length: 255)] public readonly string $logFilename, - ?array $context = [], - ) { + public function __construct(string $code, public readonly string $logFilename, ?array $context = []) + { $this->code = (string) (new UnicodeString($code))->truncate(255); $this->startDate = \DateTimeImmutable::createFromMutable(new \DateTime()); $this->context = $context ?? []; @@ -75,28 +53,19 @@ public function __toString(): string return \sprintf('%s (%s)', $this->id, $this->code); } - public function setStatus(ProcessExecutionStatus $status): void - { - $this->status = $status; - } - - public function end(): void + public function getId(): ?int { - $this->endDate = \DateTimeImmutable::createFromMutable(new \DateTime()); + return $this->id; } - public function addReport(string $key, mixed $value): void + public function getCode(): string { - $this->report[$key] = $value; + return $this->code; } - public function getReport(?string $key = null, mixed $default = null): mixed + public function end(): void { - if (null === $key) { - return $this->report; - } - - return $this->report[$key] ?? $default; + $this->endDate = \DateTimeImmutable::createFromMutable(new \DateTime()); } public function duration(string $format = '%H hour(s) %I min(s) %S s'): ?string @@ -109,24 +78,36 @@ public function duration(string $format = '%H hour(s) %I min(s) %S s'): ?string return $diff->format($format); } - public function getCode(): string + public function setStatus(ProcessExecutionStatus $status): static { - return $this->code; + $this->status = $status; + + return $this; } - /** - * @return array - */ - public function getContext(): ?array + public function addReport(string $key, mixed $value): void + { + $this->report[$key] = $value; + } + + public function getReport(?string $key = null, mixed $default = null): mixed + { + if (null === $key) { + return $this->report; + } + + return $this->report[$key] ?? $default; + } + + public function getContext(): array { return $this->context; } - /** - * @param array $context - */ - public function setContext(array $context): void + public function setContext(array $context): static { $this->context = $context; + + return $this; } } diff --git a/src/Entity/ProcessExecutionInterface.php b/src/Entity/ProcessExecutionInterface.php new file mode 100644 index 0000000..fd23bbb --- /dev/null +++ b/src/Entity/ProcessExecutionInterface.php @@ -0,0 +1,41 @@ + + */ + public function getContext(): array; + + /** + * @param array $context + */ + public function setContext(array $context): static; +} diff --git a/src/Entity/ProcessSchedule.php b/src/Entity/ProcessSchedule.php index 8eea928..e8d5045 100644 --- a/src/Entity/ProcessSchedule.php +++ b/src/Entity/ProcessSchedule.php @@ -14,45 +14,23 @@ namespace CleverAge\UiProcessBundle\Entity; use CleverAge\UiProcessBundle\Entity\Enum\ProcessScheduleType; -use CleverAge\UiProcessBundle\Repository\ProcessScheduleRepository; -use CleverAge\UiProcessBundle\Validator\CronExpression; -use CleverAge\UiProcessBundle\Validator\EveryExpression; -use CleverAge\UiProcessBundle\Validator\IsValidProcessCode; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Validator\Constraints as Assert; - -#[ORM\Entity(repositoryClass: ProcessScheduleRepository::class)] -class ProcessSchedule + +class ProcessSchedule implements ProcessScheduleInterface { - #[ORM\Id] - #[ORM\GeneratedValue] - #[ORM\Column] - private ?int $id = null; - - #[ORM\Column(length: 255)] - #[IsValidProcessCode] - private string $process; - - #[ORM\Column(length: 6)] - private ProcessScheduleType $type; - #[ORM\Column(length: 255)] - #[Assert\When( - expression: 'this.getType().value == "cron"', constraints: [new CronExpression()] - )] - #[Assert\When( - expression: 'this.getType().value == "every"', constraints: [new EveryExpression()] - )] - private string $expression; - - #[ORM\Column(type: Types::TEXT, nullable: true)] - private ?string $input = null; + protected ?int $id = null; + + protected string $process; + + protected ProcessScheduleType $type; + + protected string $expression; + + protected ?string $input = null; /** - * @var string|array + * @var array */ - #[ORM\Column(type: Types::JSON)] - private string|array $context = []; + protected array $context = []; public function getId(): ?int { @@ -71,22 +49,6 @@ public function setProcess(string $process): static return $this; } - /** - * @return array - */ - public function getContext(): array - { - return \is_array($this->context) ? $this->context : json_decode($this->context); - } - - /** - * @param array $context - */ - public function setContext(array $context): void - { - $this->context = $context; - } - public function getNextExecution(): null { return null; @@ -97,7 +59,7 @@ public function getType(): ProcessScheduleType return $this->type; } - public function setType(ProcessScheduleType $type): self + public function setType(ProcessScheduleType $type): static { $this->type = $type; @@ -109,7 +71,7 @@ public function getExpression(): ?string return $this->expression; } - public function setExpression(string $expression): self + public function setExpression(string $expression): static { $this->expression = $expression; @@ -121,10 +83,22 @@ public function getInput(): ?string return $this->input; } - public function setInput(?string $input): self + public function setInput(?string $input): static { $this->input = $input; return $this; } + + public function getContext(): array + { + return $this->context; + } + + public function setContext(array $context): static + { + $this->context = $context; + + return $this; + } } diff --git a/src/Entity/ProcessScheduleInterface.php b/src/Entity/ProcessScheduleInterface.php new file mode 100644 index 0000000..6187b7b --- /dev/null +++ b/src/Entity/ProcessScheduleInterface.php @@ -0,0 +1,47 @@ + + */ + public function getContext(): array; + + /** + * @param array $context + */ + public function setContext(array $context): static; +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 11532f7..84c1df4 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -13,47 +13,28 @@ namespace CleverAge\UiProcessBundle\Entity; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -#[ORM\Entity] -#[ORM\Table(name: 'process_user')] -#[ORM\Index(name: 'idx_process_user_email', columns: ['email'])] -class User implements UserInterface, PasswordAuthenticatedUserInterface +class User implements UserInterface { - #[ORM\Id] - #[ORM\GeneratedValue] - #[ORM\Column] - private ?int $id = null; + protected ?int $id = null; - #[ORM\Column(type: Types::STRING, length: 255, unique: true)] - private string $email; + protected string $email; - #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] - private ?string $firstname = null; + protected ?string $firstname = null; - #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] - private ?string $lastname = null; + protected ?string $lastname = null; /** * @var string[] */ - #[ORM\Column(type: Types::JSON)] - private array $roles = []; + protected array $roles = []; - #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] - private ?string $password = null; + protected ?string $password = null; - #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] - private ?string $timezone = null; + protected ?string $timezone = null; - #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] - private ?string $locale = null; + protected ?string $locale = null; - #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] - private ?string $token = null; + protected ?string $token = null; public function getId(): ?int { @@ -65,19 +46,24 @@ public function getEmail(): ?string return $this->email; } - public function setEmail(string $email): self + public function setEmail(string $email): static { $this->email = $email; return $this; } + public function getUsername(): string + { + return $this->getUserIdentifier(); + } + public function getFirstname(): ?string { return $this->firstname; } - public function setFirstname(?string $firstname): self + public function setFirstname(?string $firstname): static { $this->firstname = $firstname; @@ -89,74 +75,57 @@ public function getLastname(): ?string return $this->lastname; } - public function setLastname(?string $lastname): self + public function setLastname(?string $lastname): static { $this->lastname = $lastname; return $this; } - public function getUserIdentifier(): string - { - if ('' === $this->email) { - throw new \LogicException('The User class must have an email.'); - } - - return $this->email; - } - - public function getUsername(): string - { - return $this->getUserIdentifier(); - } - - public function getTimezone(): ?string + public function getRoles(): array { - return $this->timezone; + return array_merge(['ROLE_USER'], $this->roles); } - public function setTimezone(?string $timezone): self + public function setRoles(array $roles): static { - $this->timezone = $timezone; + $this->roles = $roles; return $this; } - public function getLocale(): ?string + public function getPassword(): ?string { - return $this->locale; + return $this->password; } - public function setLocale(?string $locale): self + public function setPassword(string $password): static { - $this->locale = $locale; + $this->password = $password; return $this; } - public function getRoles(): array + public function getTimezone(): ?string { - return array_merge(['ROLE_USER'], $this->roles); + return $this->timezone; } - /** - * @param array $roles - */ - public function setRoles(array $roles): self + public function setTimezone(?string $timezone): static { - $this->roles = $roles; + $this->timezone = $timezone; return $this; } - public function getPassword(): ?string + public function getLocale(): ?string { - return $this->password; + return $this->locale; } - public function setPassword(string $password): self + public function setLocale(?string $locale): static { - $this->password = $password; + $this->locale = $locale; return $this; } @@ -166,7 +135,7 @@ public function getToken(): ?string return $this->token; } - public function setToken(?string $token): self + public function setToken(?string $token): static { $this->token = $token; @@ -178,4 +147,13 @@ public function eraseCredentials(): void // If you store any temporary, sensitive data on the user, clear it here // $this->plainPassword = null; } + + public function getUserIdentifier(): string + { + if ('' === $this->email) { + throw new \LogicException('The User class must have an email.'); + } + + return $this->email; + } } diff --git a/src/Entity/UserInterface.php b/src/Entity/UserInterface.php new file mode 100644 index 0000000..b654054 --- /dev/null +++ b/src/Entity/UserInterface.php @@ -0,0 +1,61 @@ + $roles + */ + public function setRoles(array $roles): static; + + public function getPassword(): ?string; + + public function setPassword(string $password): static; + + public function getTimezone(): ?string; + + public function setTimezone(?string $timezone): static; + + public function getLocale(): ?string; + + public function setLocale(?string $locale): static; + + public function getToken(): ?string; + + public function setToken(?string $token): static; + + public function eraseCredentials(): void; + + public function getUserIdentifier(): string; +} diff --git a/src/EventSubscriber/ProcessEventSubscriber.php b/src/EventSubscriber/ProcessEventSubscriber.php index ed90faa..26e4759 100644 --- a/src/EventSubscriber/ProcessEventSubscriber.php +++ b/src/EventSubscriber/ProcessEventSubscriber.php @@ -15,7 +15,7 @@ use CleverAge\ProcessBundle\Event\ProcessEvent; use CleverAge\UiProcessBundle\Entity\Enum\ProcessExecutionStatus; -use CleverAge\UiProcessBundle\Entity\ProcessExecution; +use CleverAge\UiProcessBundle\Entity\ProcessExecutionInterface; use CleverAge\UiProcessBundle\Manager\ProcessExecutionManager; use CleverAge\UiProcessBundle\Monolog\Handler\DoctrineProcessHandler; use CleverAge\UiProcessBundle\Monolog\Handler\ProcessHandler; @@ -24,7 +24,11 @@ final readonly class ProcessEventSubscriber implements EventSubscriberInterface { + /** + * @param class-string $processExecutionClassName + */ public function __construct( + private string $processExecutionClassName, private ProcessHandler $processHandler, private DoctrineProcessHandler $doctrineProcessHandler, private ProcessExecutionManager $processExecutionManager, @@ -36,8 +40,8 @@ public function onProcessStart(ProcessEvent $event): void if (false === $this->processHandler->hasFilename()) { $this->processHandler->setFilename(\sprintf('%s/%s.log', $event->getProcessCode(), Uuid::v4())); } - if (!$this->processExecutionManager->getCurrentProcessExecution() instanceof ProcessExecution) { - $processExecution = new ProcessExecution( + if (!$this->processExecutionManager->getCurrentProcessExecution() instanceof ProcessExecutionInterface) { + $processExecution = new $this->processExecutionClassName( $event->getProcessCode(), basename((string) $this->processHandler->getFilename()), $event->getProcessContext() diff --git a/src/Manager/ProcessExecutionManager.php b/src/Manager/ProcessExecutionManager.php index 84a6041..66e0b37 100644 --- a/src/Manager/ProcessExecutionManager.php +++ b/src/Manager/ProcessExecutionManager.php @@ -13,34 +13,34 @@ namespace CleverAge\UiProcessBundle\Manager; -use CleverAge\UiProcessBundle\Entity\ProcessExecution; -use CleverAge\UiProcessBundle\Repository\ProcessExecutionRepository; +use CleverAge\UiProcessBundle\Entity\ProcessExecutionInterface; +use CleverAge\UiProcessBundle\Repository\ProcessExecutionRepositoryInterface; class ProcessExecutionManager { - private ?ProcessExecution $currentProcessExecution = null; + private ?ProcessExecutionInterface $currentProcessExecution = null; - public function __construct(private readonly ProcessExecutionRepository $processExecutionRepository) + public function __construct(private readonly ProcessExecutionRepositoryInterface $processExecutionRepository) { } - public function setCurrentProcessExecution(ProcessExecution $processExecution): self + public function setCurrentProcessExecution(ProcessExecutionInterface $processExecution): self { - if (!$this->currentProcessExecution instanceof ProcessExecution) { + if (!$this->currentProcessExecution instanceof ProcessExecutionInterface) { $this->currentProcessExecution = $processExecution; } return $this; } - public function getCurrentProcessExecution(): ?ProcessExecution + public function getCurrentProcessExecution(): ?ProcessExecutionInterface { return $this->currentProcessExecution; } public function unsetProcessExecution(string $processCode): self { - if ($this->currentProcessExecution?->code === $processCode) { + if ($this->currentProcessExecution?->getCode() === $processCode) { $this->currentProcessExecution = null; } @@ -49,7 +49,7 @@ public function unsetProcessExecution(string $processCode): self public function save(): self { - if ($this->currentProcessExecution instanceof ProcessExecution) { + if ($this->currentProcessExecution instanceof ProcessExecutionInterface) { $this->processExecutionRepository->save($this->currentProcessExecution); } diff --git a/src/Message/CronProcessMessage.php b/src/Message/CronProcessMessage.php index 8e94c09..11a0bfe 100644 --- a/src/Message/CronProcessMessage.php +++ b/src/Message/CronProcessMessage.php @@ -13,11 +13,11 @@ namespace CleverAge\UiProcessBundle\Message; -use CleverAge\UiProcessBundle\Entity\ProcessSchedule; +use CleverAge\UiProcessBundle\Entity\ProcessScheduleInterface; final readonly class CronProcessMessage { - public function __construct(public ProcessSchedule $processSchedule) + public function __construct(public ProcessScheduleInterface $processSchedule) { } } diff --git a/src/Monolog/Handler/DoctrineProcessHandler.php b/src/Monolog/Handler/DoctrineProcessHandler.php index 07c4a0a..28c1eef 100644 --- a/src/Monolog/Handler/DoctrineProcessHandler.php +++ b/src/Monolog/Handler/DoctrineProcessHandler.php @@ -13,7 +13,8 @@ namespace CleverAge\UiProcessBundle\Monolog\Handler; -use CleverAge\UiProcessBundle\Entity\ProcessExecution; +use CleverAge\UiProcessBundle\Entity\LogRecordInterface; +use CleverAge\UiProcessBundle\Entity\ProcessExecutionInterface; use CleverAge\UiProcessBundle\Manager\ProcessExecutionManager; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; @@ -27,6 +28,8 @@ class DoctrineProcessHandler extends AbstractProcessingHandler private ArrayCollection $records; private ?ProcessExecutionManager $processExecutionManager = null; private ?EntityManagerInterface $em = null; + /** @var string class-string */ + private string $logRecordClassName; public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true) { @@ -44,6 +47,11 @@ public function setProcessExecutionManager(ProcessExecutionManager $processExecu $this->processExecutionManager = $processExecutionManager; } + public function setLogRecordClassName(string $logRecordClassName): void + { + $this->logRecordClassName = $logRecordClassName; + } + public function __destruct() { $this->flush(); @@ -53,8 +61,8 @@ public function __destruct() public function flush(): void { foreach ($this->records as $record) { - if (($currentProcessExecution = $this->processExecutionManager?->getCurrentProcessExecution()) instanceof ProcessExecution) { - $entity = new \CleverAge\UiProcessBundle\Entity\LogRecord($record, $currentProcessExecution); + if (($currentProcessExecution = $this->processExecutionManager?->getCurrentProcessExecution()) instanceof ProcessExecutionInterface) { + $entity = new $this->logRecordClassName($record, $currentProcessExecution); $this->em?->persist($entity); } } diff --git a/src/Repository/ProcessExecutionRepository.php b/src/Repository/ProcessExecutionRepository.php index e5d09e0..13da738 100644 --- a/src/Repository/ProcessExecutionRepository.php +++ b/src/Repository/ProcessExecutionRepository.php @@ -13,33 +13,32 @@ namespace CleverAge\UiProcessBundle\Repository; -use CleverAge\UiProcessBundle\Entity\LogRecord; -use CleverAge\UiProcessBundle\Entity\ProcessExecution; +use CleverAge\UiProcessBundle\Entity\ProcessExecutionInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; /** - * @extends EntityRepository + * @template T of ProcessExecutionInterface * - * @method ProcessExecution|null find($id, $lockMode = null, $lockVersion = null) - * @method ProcessExecution|null findOneBy(mixed[] $criteria, string[] $orderBy = null) - * @method ProcessExecution[] findAll() - * @method ProcessExecution[] findBy(mixed[] $criteria, string[] $orderBy = null, $limit = null, $offset = null) + * @template-extends EntityRepository */ -class ProcessExecutionRepository extends EntityRepository +class ProcessExecutionRepository extends EntityRepository implements ProcessExecutionRepositoryInterface { - public function __construct(EntityManagerInterface $em) + /** + * @param class-string $className + */ + public function __construct(EntityManagerInterface $em, string $className, private readonly string $logRecordClassName) { - parent::__construct($em, $em->getClassMetadata(ProcessExecution::class)); + parent::__construct($em, $em->getClassMetadata($className)); } - public function save(ProcessExecution $processExecution): void + public function save(ProcessExecutionInterface $processExecution): void { $this->getEntityManager()->persist($processExecution); $this->getEntityManager()->flush(); } - public function getLastProcessExecution(string $code): ?ProcessExecution + public function getLastProcessExecution(string $code): ?ProcessExecutionInterface { $qb = $this->createQueryBuilder('pe'); @@ -51,11 +50,11 @@ public function getLastProcessExecution(string $code): ?ProcessExecution ->getOneOrNullResult(); } - public function hasLogs(ProcessExecution $processExecution): bool + public function hasLogs(ProcessExecutionInterface $processExecution): bool { $qb = $this->createQueryBuilder('pe') ->select('count(lr.id)') - ->join(LogRecord::class, 'lr', 'WITH', 'lr.processExecution = pe') + ->join($this->logRecordClassName, 'lr', 'WITH', 'lr.processExecution = pe') ->where('pe.id = :id') ->setParameter('id', $processExecution->getId() ); diff --git a/src/Repository/ProcessExecutionRepositoryInterface.php b/src/Repository/ProcessExecutionRepositoryInterface.php new file mode 100644 index 0000000..ae980d3 --- /dev/null +++ b/src/Repository/ProcessExecutionRepositoryInterface.php @@ -0,0 +1,27 @@ + + */ +interface ProcessExecutionRepositoryInterface extends ObjectRepository +{ + public function save(ProcessExecutionInterface $processExecution): void; + + public function getLastProcessExecution(string $code): ?ProcessExecutionInterface; + + public function hasLogs(ProcessExecutionInterface $processExecution): bool; +} diff --git a/src/Repository/ProcessScheduleRepository.php b/src/Repository/ProcessScheduleRepository.php index 6c8e3ea..5e6be50 100644 --- a/src/Repository/ProcessScheduleRepository.php +++ b/src/Repository/ProcessScheduleRepository.php @@ -13,22 +13,22 @@ namespace CleverAge\UiProcessBundle\Repository; -use CleverAge\UiProcessBundle\Entity\ProcessSchedule; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; -use Doctrine\Persistence\ManagerRegistry; +use CleverAge\UiProcessBundle\Entity\ProcessScheduleInterface; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; /** - * @extends ServiceEntityRepository + * @template T of ProcessScheduleInterface * - * @method ProcessSchedule|null find($id, $lockMode = null, $lockVersion = null) - * @method ProcessSchedule|null findOneBy(mixed[] $criteria, string[] $orderBy = null) - * @method ProcessSchedule[] findAll() - * @method ProcessSchedule[] findBy(mixed[] $criteria, string[] $orderBy = null, $limit = null, $offset = null) + * @template-extends EntityRepository */ -class ProcessScheduleRepository extends ServiceEntityRepository +class ProcessScheduleRepository extends EntityRepository implements ProcessScheduleRepositoryInterface { - public function __construct(ManagerRegistry $registry) + /** + * @param class-string $className + */ + public function __construct(EntityManagerInterface $em, string $className) { - parent::__construct($registry, ProcessSchedule::class); + parent::__construct($em, $em->getClassMetadata($className)); } } diff --git a/src/Repository/ProcessScheduleRepositoryInterface.php b/src/Repository/ProcessScheduleRepositoryInterface.php new file mode 100644 index 0000000..443a046 --- /dev/null +++ b/src/Repository/ProcessScheduleRepositoryInterface.php @@ -0,0 +1,22 @@ + + */ +interface ProcessScheduleRepositoryInterface extends ObjectRepository +{ +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..4a5a9a3 --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,34 @@ + + */ +class UserRepository extends EntityRepository implements UserRepositoryInterface +{ + /** + * @param class-string $className + */ + public function __construct(EntityManagerInterface $em, string $className) + { + parent::__construct($em, $em->getClassMetadata($className)); + } +} diff --git a/src/Repository/UserRepositoryInterface.php b/src/Repository/UserRepositoryInterface.php new file mode 100644 index 0000000..9fb3f40 --- /dev/null +++ b/src/Repository/UserRepositoryInterface.php @@ -0,0 +1,22 @@ + + */ +interface UserRepositoryInterface extends ObjectRepository +{ +} diff --git a/src/Scheduler/CronScheduler.php b/src/Scheduler/CronScheduler.php index b12fd20..518309d 100644 --- a/src/Scheduler/CronScheduler.php +++ b/src/Scheduler/CronScheduler.php @@ -15,7 +15,7 @@ use CleverAge\UiProcessBundle\Entity\Enum\ProcessScheduleType; use CleverAge\UiProcessBundle\Message\CronProcessMessage; -use CleverAge\UiProcessBundle\Repository\ProcessScheduleRepository; +use CleverAge\UiProcessBundle\Repository\ProcessScheduleRepositoryInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Scheduler\RecurringMessage; use Symfony\Component\Scheduler\Schedule; @@ -25,7 +25,7 @@ readonly class CronScheduler implements ScheduleProviderInterface { public function __construct( - private ProcessScheduleRepository $repository, + private ProcessScheduleRepositoryInterface $processScheduleRepository, private ValidatorInterface $validator, private LoggerInterface $logger, ) { @@ -35,7 +35,7 @@ public function getSchedule(): Schedule { $schedule = new Schedule(); try { - foreach ($this->repository->findAll() as $processSchedule) { + foreach ($this->processScheduleRepository->findAll() as $processSchedule) { $violations = $this->validator->validate($processSchedule); if (0 !== $violations->count()) { foreach ($violations as $violation) { diff --git a/src/Security/HttpProcessExecutionAuthenticator.php b/src/Security/HttpProcessExecutionAuthenticator.php index 85734f9..5ce8962 100644 --- a/src/Security/HttpProcessExecutionAuthenticator.php +++ b/src/Security/HttpProcessExecutionAuthenticator.php @@ -13,8 +13,7 @@ namespace CleverAge\UiProcessBundle\Security; -use CleverAge\UiProcessBundle\Entity\User; -use Doctrine\ORM\EntityManagerInterface; +use CleverAge\UiProcessBundle\Repository\UserRepositoryInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -28,7 +27,7 @@ class HttpProcessExecutionAuthenticator extends AbstractAuthenticator { - public function __construct(private readonly EntityManagerInterface $entityManager) + public function __construct(private readonly UserRepositoryInterface $userRepository) { } @@ -44,7 +43,7 @@ public function authenticate(Request $request): Passport } $token = $request->headers->get('Authorization'); $token = str_replace('Bearer ', '', $token ?? ''); - $user = $this->entityManager->getRepository(User::class)->findOneBy( + $user = $this->userRepository->findOneBy( ['token' => (new Pbkdf2PasswordHasher())->hash($token)] ); if (null === $user) { diff --git a/src/Twig/Runtime/ProcessExecutionExtensionRuntime.php b/src/Twig/Runtime/ProcessExecutionExtensionRuntime.php index 592f1e4..e0e43fd 100644 --- a/src/Twig/Runtime/ProcessExecutionExtensionRuntime.php +++ b/src/Twig/Runtime/ProcessExecutionExtensionRuntime.php @@ -13,20 +13,20 @@ namespace CleverAge\UiProcessBundle\Twig\Runtime; -use CleverAge\UiProcessBundle\Entity\ProcessExecution; +use CleverAge\UiProcessBundle\Entity\ProcessExecutionInterface; use CleverAge\UiProcessBundle\Manager\ProcessConfigurationsManager; -use CleverAge\UiProcessBundle\Repository\ProcessExecutionRepository; +use CleverAge\UiProcessBundle\Repository\ProcessExecutionRepositoryInterface; use Twig\Extension\RuntimeExtensionInterface; readonly class ProcessExecutionExtensionRuntime implements RuntimeExtensionInterface { public function __construct( - private ProcessExecutionRepository $processExecutionRepository, + private ProcessExecutionRepositoryInterface $processExecutionRepository, private ProcessConfigurationsManager $processConfigurationsManager, ) { } - public function getLastExecutionDate(string $code): ?ProcessExecution + public function getLastExecutionDate(string $code): ?ProcessExecutionInterface { return $this->processExecutionRepository->getLastProcessExecution($code); }