From c99360c200b42e81ac1e0c195ecf210e93d7bfa7 Mon Sep 17 00:00:00 2001 From: Adrian Dumitrache Date: Fri, 12 Apr 2024 00:04:40 +0300 Subject: [PATCH] cron-job package --- .github/workflows/after_splitting_test.yaml | 1 + composer.json | 2 + packages/cron-job/.gitignore | 8 + packages/cron-job/Entity/CronJob.php | 169 ++++++++++++++++++ packages/cron-job/Entity/CronJobExecution.php | 157 ++++++++++++++++ .../Message/ExecuteCronJobMessage.php | 33 ++++ .../ExecuteCronJobMessageHandler.php | 21 +++ .../ExecuteCronJobMessageHandlerTest.php | 19 ++ packages/cron-job/composer.json | 31 ++++ packages/cron-job/phpunit.xml.dist | 9 + .../DrawFrameworkExtraExtension.php | 2 + .../Integration/CronJobIntegration.php | 70 ++++++++ 12 files changed, 522 insertions(+) create mode 100644 packages/cron-job/.gitignore create mode 100644 packages/cron-job/Entity/CronJob.php create mode 100644 packages/cron-job/Entity/CronJobExecution.php create mode 100644 packages/cron-job/Message/ExecuteCronJobMessage.php create mode 100644 packages/cron-job/MessageHandler/ExecuteCronJobMessageHandler.php create mode 100644 packages/cron-job/Tests/MessageHandler/ExecuteCronJobMessageHandlerTest.php create mode 100644 packages/cron-job/composer.json create mode 100644 packages/cron-job/phpunit.xml.dist create mode 100644 packages/framework-extra-bundle/DependencyInjection/Integration/CronJobIntegration.php diff --git a/.github/workflows/after_splitting_test.yaml b/.github/workflows/after_splitting_test.yaml index 9995aa3e4..889be2eaf 100644 --- a/.github/workflows/after_splitting_test.yaml +++ b/.github/workflows/after_splitting_test.yaml @@ -18,6 +18,7 @@ jobs: - aws-tool-kit - console - core + - cron-job - doctrine-extra - entity-migrator - fixer diff --git a/composer.json b/composer.json index 29d380a67..9de8b3990 100644 --- a/composer.json +++ b/composer.json @@ -126,6 +126,7 @@ "draw/console": "self.version", "draw/contracts": "self.version", "draw/core": "self.version", + "draw/cron-job": "self.version", "draw/doctrine-extra": "self.version", "draw/entity-migrator": "self.version", "draw/fixer": "self.version", @@ -172,6 +173,7 @@ "Draw\\Component\\AwsToolKit\\": "packages/aws-tool-kit/", "Draw\\Component\\Console\\": "packages/console/", "Draw\\Component\\Core\\": "packages/core/", + "Draw\\Component\\CronJob\\": "packages/cron-job/", "Draw\\Component\\EntityMigrator\\": "packages/entity-migrator/", "Draw\\Component\\Log\\": "packages/log/", "Draw\\Component\\Mailer\\": "packages/mailer/", diff --git a/packages/cron-job/.gitignore b/packages/cron-job/.gitignore new file mode 100644 index 000000000..17ae66f14 --- /dev/null +++ b/packages/cron-job/.gitignore @@ -0,0 +1,8 @@ +/vendor/ +/.idea/ +composer.lock + +###> phpunit/phpunit ### +/phpunit.xml +.phpunit.result.cache +###< phpunit/phpunit ### diff --git a/packages/cron-job/Entity/CronJob.php b/packages/cron-job/Entity/CronJob.php new file mode 100644 index 000000000..ddd472d7c --- /dev/null +++ b/packages/cron-job/Entity/CronJob.php @@ -0,0 +1,169 @@ + false])] + private bool $active = false; + + #[ORM\Column(name: 'command', type: 'string', length: 255, nullable: false)] + private ?string $command = null; + + #[ORM\Column(name: 'schedule', type: 'string', length: 255, nullable: true)] + private ?string $schedule = null; + + #[ORM\Column(name: 'ttl', type: 'int', nullable: false, options: ['default' => 0])] + private int $ttl = 0; + + #[ORM\Column(name: 'priority', type: 'int', nullable: true)] + private ?int $priority = null; + + /** + * @var Collection + */ + #[ + ORM\OneToMany( + mappedBy: 'cronJob', + targetEntity: CronJobExecution::class, + cascade: ['persist'], + orphanRemoval: true, + ) + ] + private Collection $executions; + + public function __construct() + { + $this->executions = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(?string $name): self + { + $this->name = $name; + + return $this; + } + + public function isActive(): bool + { + return $this->active; + } + + public function setActive(bool $active): self + { + $this->active = $active; + + return $this; + } + + public function getCommand(): ?string + { + return $this->command; + } + + public function setCommand(?string $command): self + { + $this->command = $command; + + return $this; + } + + public function getSchedule(): ?string + { + return $this->schedule; + } + + public function setSchedule(?string $schedule): self + { + $this->schedule = $schedule; + + return $this; + } + + public function getTtl(): int + { + return $this->ttl; + } + + public function setTtl(int $ttl): self + { + $this->ttl = $ttl; + + return $this; + } + + public function getPriority(): ?int + { + return $this->priority; + } + + public function setPriority(?int $priority): self + { + $this->priority = $priority; + + return $this; + } + + /** + * @return Collection + */ + public function getExecutions(): Collection + { + return $this->executions; + } + + public function addExecution(CronJobExecution $execution): self + { + if (!$this->executions->contains($execution)) { + $this->executions->add($execution); + $execution->setCronJob($this); + } + + return $this; + } + + public function removeExecution(CronJobExecution $execution): self + { + if ($this->executions->contains($execution)) { + $this->executions->removeElement($execution); + $execution->setCronJob(null); + } + + return $this; + } + + public function __toString(): string + { + return $this->name; + } +} diff --git a/packages/cron-job/Entity/CronJobExecution.php b/packages/cron-job/Entity/CronJobExecution.php new file mode 100644 index 000000000..064b13861 --- /dev/null +++ b/packages/cron-job/Entity/CronJobExecution.php @@ -0,0 +1,157 @@ + false])] + private bool $force = false; + + #[ORM\Column(name: 'execution_started_at', type: 'datetime_immutable', nullable: true)] + private ?\DateTimeImmutable $executionStartedAt = null; + + #[ORM\Column(name: 'execution_ended_at', type: 'datetime_immutable', nullable: true)] + private ?\DateTimeImmutable $executionEndedAt = null; + + #[ORM\Column(name: 'execution_delay', type: 'int', nullable: true)] + private ?int $executionDelay = null; + + #[ORM\Column(name: 'exit_code', type: 'int', nullable: true)] + private ?int $exitCode = null; + + #[ORM\Column(name: 'error', type: 'json', nullable: true)] + private ?array $error = null; + + #[ + ORM\ManyToOne( + targetEntity: CronJob::class, + inversedBy: 'executions', + ), + ORM\JoinColumn( + name: 'cron_job_id', + referencedColumnName: 'id', + nullable: false, + onDelete: 'CASCADE', + ) + ] + private ?CronJob $cronJob = null; + + public function getId(): ?int + { + return $this->id; + } + + public function getRequestedAt(): ?\DateTimeImmutable + { + return $this->requestedAt; + } + + public function setRequestedAt(?\DateTimeImmutable $requestedAt): self + { + $this->requestedAt = $requestedAt; + + return $this; + } + + public function isForce(): bool + { + return $this->force; + } + + public function setForce(bool $force): self + { + $this->force = $force; + + return $this; + } + + public function getExecutionStartedAt(): ?\DateTimeImmutable + { + return $this->executionStartedAt; + } + + public function setExecutionStartedAt(?\DateTimeImmutable $executionStartedAt): self + { + $this->executionStartedAt = $executionStartedAt; + + return $this; + } + + public function getExecutionEndedAt(): ?\DateTimeImmutable + { + return $this->executionEndedAt; + } + + public function setExecutionEndedAt(?\DateTimeImmutable $executionEndedAt): self + { + $this->executionEndedAt = $executionEndedAt; + + return $this; + } + + public function getExecutionDelay(): ?int + { + return $this->executionDelay; + } + + public function setExecutionDelay(?int $executionDelay): self + { + $this->executionDelay = $executionDelay; + + return $this; + } + + public function getExitCode(): ?int + { + return $this->exitCode; + } + + public function setExitCode(?int $exitCode): self + { + $this->exitCode = $exitCode; + + return $this; + } + + public function getError(): ?array + { + return $this->error; + } + + public function setError(?array $error): self + { + $this->error = $error; + + return $this; + } + + public function getCronJob(): ?CronJob + { + return $this->cronJob; + } + + public function setCronJob(?CronJob $cronJob): self + { + $this->cronJob = $cronJob; + + return $this; + } +} diff --git a/packages/cron-job/Message/ExecuteCronJobMessage.php b/packages/cron-job/Message/ExecuteCronJobMessage.php new file mode 100644 index 000000000..9c3df60b4 --- /dev/null +++ b/packages/cron-job/Message/ExecuteCronJobMessage.php @@ -0,0 +1,33 @@ +cronJob = $cronJob; + } + + public function getCronJob(): ?CronJob + { + if (null === $this->cronJob) { + throw new UnrecoverableMessageHandlingException('CronJob is not set.'); + } + + return $this->cronJob; + } + + public function getPropertiesWithDoctrineObject(): array + { + return ['cronJob']; + } +} diff --git a/packages/cron-job/MessageHandler/ExecuteCronJobMessageHandler.php b/packages/cron-job/MessageHandler/ExecuteCronJobMessageHandler.php new file mode 100644 index 000000000..6dacd4dd0 --- /dev/null +++ b/packages/cron-job/MessageHandler/ExecuteCronJobMessageHandler.php @@ -0,0 +1,21 @@ +=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "Draw\\Component\\CronJob\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.11-dev" + } + } +} diff --git a/packages/cron-job/phpunit.xml.dist b/packages/cron-job/phpunit.xml.dist new file mode 100644 index 000000000..4134c1453 --- /dev/null +++ b/packages/cron-job/phpunit.xml.dist @@ -0,0 +1,9 @@ + + + + ./Tests + + + diff --git a/packages/framework-extra-bundle/DependencyInjection/DrawFrameworkExtraExtension.php b/packages/framework-extra-bundle/DependencyInjection/DrawFrameworkExtraExtension.php index 2d6ad3887..962bae2ae 100644 --- a/packages/framework-extra-bundle/DependencyInjection/DrawFrameworkExtraExtension.php +++ b/packages/framework-extra-bundle/DependencyInjection/DrawFrameworkExtraExtension.php @@ -6,6 +6,7 @@ use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\ConfigurationIntegration; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\ConsoleIntegration; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\CronIntegration; +use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\CronJobIntegration; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\DoctrineExtraIntegration; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\EntityMigratorIntegration; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\FeatureIntegration; @@ -52,6 +53,7 @@ private function registerDefaultIntegrations(): void $this->integrations[] = new ConfigurationIntegration(); $this->integrations[] = new ConsoleIntegration(); $this->integrations[] = new CronIntegration(); + $this->integrations[] = new CronJobIntegration(); $this->integrations[] = new DoctrineExtraIntegration(); $this->integrations[] = new EntityMigratorIntegration(); $this->integrations[] = new FeatureIntegration(); diff --git a/packages/framework-extra-bundle/DependencyInjection/Integration/CronJobIntegration.php b/packages/framework-extra-bundle/DependencyInjection/Integration/CronJobIntegration.php new file mode 100644 index 000000000..8e2346963 --- /dev/null +++ b/packages/framework-extra-bundle/DependencyInjection/Integration/CronJobIntegration.php @@ -0,0 +1,70 @@ +registerClasses( + $loader, + $namespace = 'Draw\\Component\\CronJob\\', + $directory = \dirname( + (new \ReflectionClass(PurgeExecutionCommand::class))->getFileName(), + 2 + ), + [ + $directory.'/Output/', + ] + ); + + $this->renameDefinitions( + $container, + $namespace, + 'draw.console.' + ); + } + + public function addConfiguration(ArrayNodeDefinition $node): void + { + // @TODO + } + + public function prepend(ContainerBuilder $container, array $config): void + { + $this->assertHasExtension($container, 'doctrine'); + + $reflection = new \ReflectionClass(Execution::class); + + $container->prependExtensionConfig( + 'doctrine', + [ + 'orm' => [ + 'mappings' => [ + 'DrawCronJob' => [ + 'is_bundle' => false, + 'type' => 'attribute', + 'dir' => \dirname($reflection->getFileName()), + 'prefix' => $reflection->getNamespaceName(), + ], + ], + ], + ] + ); + } +}