From 43af43b7199c760a79232874939e36594c3c1803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Poirier=20Th=C3=A9or=C3=AAt?= Date: Sun, 21 Apr 2024 16:12:59 -0400 Subject: [PATCH] Remove not need mutator method or make them private. Update documentation Setup messenger configuration for sandbox --- config/packages/messenger.yaml | 1 + .../Command/QueueDueCronJobsCommand.php | 2 +- packages/cron-job/CronJobProcessor.php | 2 +- packages/cron-job/Entity/CronJob.php | 40 ++++----- packages/cron-job/Entity/CronJobExecution.php | 56 ++++++------- packages/cron-job/README.md | 17 +++- .../Command/QueueDueCronJobsCommandTest.php | 2 +- .../cron-job/Tests/CronJobProcessorTest.php | 19 ++--- .../Tests/Entity/CronJobExecutionTest.php | 16 ++-- .../CronJob/Controller/CronJobController.php | 2 +- vendor-bin/monorepo/composer.lock | 84 +++++++++---------- 11 files changed, 119 insertions(+), 122 deletions(-) diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml index d0a403a2..dbdb4055 100644 --- a/config/packages/messenger.yaml +++ b/config/packages/messenger.yaml @@ -16,6 +16,7 @@ framework: sync: 'sync://' routing: + Draw\Component\CronJob\Message\ExecuteCronJobMessage: 'async_high_priority' App\Message\NewTestDocumentMessage: ['sync', 'async'] Draw\Component\Messenger\ManualTrigger\Message\ManuallyTriggeredInterface: 'async' Draw\Bundle\UserBundle\Message\NewUserLockMessage: 'sync' diff --git a/packages/cron-job/Command/QueueDueCronJobsCommand.php b/packages/cron-job/Command/QueueDueCronJobsCommand.php index bb0664c7..5dfbc31b 100644 --- a/packages/cron-job/Command/QueueDueCronJobsCommand.php +++ b/packages/cron-job/Command/QueueDueCronJobsCommand.php @@ -45,7 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->note(sprintf('Queueing cron job "%s"...', $cronJob->getName())); - $this->cronJobProcessor->queue($cronJob); + $this->cronJobProcessor->queue($cronJob, false); } $io->success('Cron jobs successfully queued...'); diff --git a/packages/cron-job/CronJobProcessor.php b/packages/cron-job/CronJobProcessor.php index 35f3fba2..eb86fe43 100644 --- a/packages/cron-job/CronJobProcessor.php +++ b/packages/cron-job/CronJobProcessor.php @@ -26,7 +26,7 @@ public function __construct( ) { } - public function queue(CronJob $cronJob, bool $force = false): void + public function queue(CronJob $cronJob, bool $force): void { $manager = $this->managerRegistry->getManagerForClass(CronJobExecution::class); diff --git a/packages/cron-job/Entity/CronJob.php b/packages/cron-job/Entity/CronJob.php index b4e39e72..8d3aa6c0 100644 --- a/packages/cron-job/Entity/CronJob.php +++ b/packages/cron-job/Entity/CronJob.php @@ -8,6 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Validator\Constraints as Assert; #[ ORM\Entity, @@ -23,21 +24,31 @@ class CronJob implements \Stringable private ?int $id = null; #[ORM\Column(name: 'name', type: 'string', length: 255, unique: true, nullable: false)] + #[ + Assert\NotNull, + Assert\Length(min: 1, max: 255), + ] private ?string $name = null; #[ORM\Column(name: 'active', type: 'boolean', nullable: false, options: ['default' => false])] private bool $active = false; #[ORM\Column(name: 'command', type: 'text', nullable: false)] + #[Assert\NotNull] private ?string $command = null; #[ORM\Column(name: 'schedule', type: 'string', length: 255, nullable: true)] private ?string $schedule = null; #[ORM\Column(name: 'time_to_live', type: 'integer', nullable: false, options: ['default' => 0])] + #[ + Assert\NotNull, + Assert\GreaterThanOrEqual(0), + ] private int $timeToLive = 0; #[ORM\Column(name: 'priority', type: 'integer', nullable: true)] + #[Assert\Range(min: 0, max: 255)] private ?int $priority = null; /** @@ -148,26 +159,6 @@ 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 isDue(): bool { if (null === $this->getSchedule()) { @@ -179,10 +170,11 @@ public function isDue(): bool public function newExecution(bool $force = false): CronJobExecution { - return (new CronJobExecution()) - ->setCronJob($this) - ->setRequestedAt(new \DateTimeImmutable()) - ->setForce($force); + $cronJobExecution = new CronJobExecution($this, new \DateTimeImmutable(), $force); + + $this->executions->add($cronJobExecution); + + return $cronJobExecution; } public function __toString(): string diff --git a/packages/cron-job/Entity/CronJobExecution.php b/packages/cron-job/Entity/CronJobExecution.php index 3edcc379..b7661d93 100644 --- a/packages/cron-job/Entity/CronJobExecution.php +++ b/packages/cron-job/Entity/CronJobExecution.php @@ -20,7 +20,7 @@ class CronJobExecution implements \Stringable private ?int $id = null; #[ORM\Column(name: 'requested_at', type: 'datetime_immutable', nullable: false)] - private ?\DateTimeImmutable $requestedAt = null; + private \DateTimeImmutable $requestedAt; #[ORM\Column(name: '`force`', type: 'boolean', nullable: false, options: ['default' => false])] private bool $force = false; @@ -52,7 +52,17 @@ class CronJobExecution implements \Stringable onDelete: 'CASCADE', ) ] - private ?CronJob $cronJob = null; + private CronJob $cronJob; + + public function __construct( + CronJob $cronJob, + \DateTimeImmutable $requestedAt, + bool $force + ) { + $this->cronJob = $cronJob; + $this->requestedAt = $requestedAt; + $this->force = $force; + } public function getId(): ?int { @@ -64,31 +74,17 @@ 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 + private function setExecutionStartedAt(?\DateTimeImmutable $executionStartedAt): self { $this->executionStartedAt = $executionStartedAt; @@ -100,7 +96,7 @@ public function getExecutionEndedAt(): ?\DateTimeImmutable return $this->executionEndedAt; } - public function setExecutionEndedAt(?\DateTimeImmutable $executionEndedAt): self + private function setExecutionEndedAt(?\DateTimeImmutable $executionEndedAt): self { $this->executionEndedAt = $executionEndedAt; @@ -112,7 +108,7 @@ public function getExecutionDelay(): ?int return $this->executionDelay; } - public function setExecutionDelay(?int $executionDelay): self + private function setExecutionDelay(?int $executionDelay): self { $this->executionDelay = $executionDelay; @@ -124,7 +120,7 @@ public function getExitCode(): ?int return $this->exitCode; } - public function setExitCode(?int $exitCode): self + private function setExitCode(?int $exitCode): self { $this->exitCode = $exitCode; @@ -136,7 +132,7 @@ public function getError(): ?array return $this->error; } - public function setError(?array $error): self + private function setError(?array $error): self { $this->error = $error; @@ -148,13 +144,6 @@ public function getCronJob(): ?CronJob return $this->cronJob; } - public function setCronJob(?CronJob $cronJob): self - { - $this->cronJob = $cronJob; - - return $this; - } - public function isExecutable(\DateTimeImmutable $dateTime): bool { if (!($cronJob = $this->getCronJob())?->isActive()) { @@ -179,19 +168,22 @@ public function start(): void ->setExecutionEndedAt(null); } - public function end(): void + public function end(): static { $this ->setExitCode(0) ->setExecutionEndedAt($executionEndedAt = new \DateTimeImmutable()) - ->setExecutionDelay($executionEndedAt->getTimestamp() - $this->getExecutionStartedAt()->getTimestamp()); + ->setExecutionDelay( + $executionEndedAt->getTimestamp() - $this->getExecutionStartedAt()->getTimestamp() + ); + + return $this; } public function fail(?int $exitCode, ?array $error): void { - $this->end(); - $this + ->end() ->setExitCode($exitCode) ->setError($error); } diff --git a/packages/cron-job/README.md b/packages/cron-job/README.md index 8738c453..441c3533 100644 --- a/packages/cron-job/README.md +++ b/packages/cron-job/README.md @@ -1,6 +1,8 @@ # Cron Job -This library is used to manage and process cron jobs. +This library is used to manage and process cron jobs from the database. + +The cron are sent to a queue and processed by a worker via symfony messenger. ## Configuration @@ -57,9 +59,20 @@ draw_sonata_integration: translation_domain: SonataAdminBundle ``` +### Messenger + +You need to configure the routing for the messenger component for the message that will be used to process the cron jobs. + +```yaml +framework: + messenger: + routing: + Draw\Component\CronJob\Message\ExecuteCronJobMessage: 'async' +``` + ## Usage Once the package is enabled, a new admin page will be available - **Cron Job**. The package also provides 2 console commands: -- **draw:cron-job:queue-due** - it is used to process due cron jobs by their configs; it can be configured in the **crontab** +- **draw:cron-job:queue-due** - it is used to process due cron jobs by their configs; it should be configured as a cron to be executed with * * * * * - **draw:cron-job:queue-by-name** - it allows to manually process a cron job by its name passed as an argument diff --git a/packages/cron-job/Tests/Command/QueueDueCronJobsCommandTest.php b/packages/cron-job/Tests/Command/QueueDueCronJobsCommandTest.php index ee94ff4d..e7805b18 100644 --- a/packages/cron-job/Tests/Command/QueueDueCronJobsCommandTest.php +++ b/packages/cron-job/Tests/Command/QueueDueCronJobsCommandTest.php @@ -93,7 +93,7 @@ public function testExecute(array $rawCronJobs, array $expectedDisplay): void ->method('queue') ->with( ...static::withConsecutive(...array_map( - static fn (CronJob $cronJob): array => [$cronJob], + static fn (CronJob $cronJob): array => [$cronJob, false], $dueCronJobs )) ); diff --git a/packages/cron-job/Tests/CronJobProcessorTest.php b/packages/cron-job/Tests/CronJobProcessorTest.php index 817e5d2f..7ef6e24e 100644 --- a/packages/cron-job/Tests/CronJobProcessorTest.php +++ b/packages/cron-job/Tests/CronJobProcessorTest.php @@ -31,8 +31,6 @@ class CronJobProcessorTest extends TestCase private CronJobProcessor $cronJobProcessor; - private ManagerRegistry&MockObject $managerRegistry; - private EventDispatcherInterface&MockObject $eventDispatcher; private ProcessFactoryInterface&MockObject $processFactory; @@ -43,17 +41,15 @@ class CronJobProcessorTest extends TestCase protected function setUp(): void { - parent::setUp(); - - $this->managerRegistry = $this->createMock(ManagerRegistry::class); - $this->managerRegistry + $managerRegistry = $this->createMock(ManagerRegistry::class); + $managerRegistry ->expects(static::any()) ->method('getManagerForClass') ->with(CronJobExecution::class) ->willReturn($this->entityManager = $this->createMock(EntityManagerInterface::class)); $this->cronJobProcessor = new CronJobProcessor( - $this->managerRegistry, + $managerRegistry, new ParameterBag([ 'kernel.cache_dir' => '/var/cache', ]), @@ -261,9 +257,10 @@ public function testProcessWithCancelledExecution(): void private function createCronJobExecution(string $command = 'bin/console draw:test:execute'): CronJobExecution { - return (new CronJobExecution()) - ->setCronJob( - (new CronJob())->setCommand($command) - ); + return new CronJobExecution( + (new CronJob())->setCommand($command), + new \DateTimeImmutable(), + false + ); } } diff --git a/packages/cron-job/Tests/Entity/CronJobExecutionTest.php b/packages/cron-job/Tests/Entity/CronJobExecutionTest.php index d47e4f9e..a3169b96 100644 --- a/packages/cron-job/Tests/Entity/CronJobExecutionTest.php +++ b/packages/cron-job/Tests/Entity/CronJobExecutionTest.php @@ -24,13 +24,13 @@ public function testIsExecutable( ): void { Carbon::setTestNow($now); - $execution = (new CronJobExecution()) - ->setRequestedAt($requestedAt) - ->setCronJob( - (new CronJob()) - ->setActive($active) - ->setTimeToLive($timeToLive) - ); + $execution = new CronJobExecution( + (new CronJob()) + ->setActive($active) + ->setTimeToLive($timeToLive), + $requestedAt, + false + ); static::assertSame($expectedExecutable, $execution->isExecutable(Carbon::now()->toDateTimeImmutable())); } @@ -40,6 +40,8 @@ public static function provideDataForTestIsExecutable(): iterable yield 'inactive' => [ '$expectedExecutable' => false, '$active' => false, + '$timeToLive' => 0, + '$requestedAt' => new \DateTimeImmutable('2024-04-17 00:00:00'), ]; yield 'active with no time to live' => [ diff --git a/packages/sonata-integration-bundle/CronJob/Controller/CronJobController.php b/packages/sonata-integration-bundle/CronJob/Controller/CronJobController.php index cd3cb449..028db644 100644 --- a/packages/sonata-integration-bundle/CronJob/Controller/CronJobController.php +++ b/packages/sonata-integration-bundle/CronJob/Controller/CronJobController.php @@ -17,7 +17,7 @@ public function queueAction( CronJob $cronJob, CronJobProcessor $cronJobProcessor ): Response { - $cronJobProcessor->queue($cronJob); + $cronJobProcessor->queue($cronJob, true); $this->addFlash( 'sonata_flash_success', diff --git a/vendor-bin/monorepo/composer.lock b/vendor-bin/monorepo/composer.lock index 6c504e40..b88f4ff9 100644 --- a/vendor-bin/monorepo/composer.lock +++ b/vendor-bin/monorepo/composer.lock @@ -488,16 +488,16 @@ }, { "name": "symfony/config", - "version": "v5.4.36", + "version": "v5.4.38", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "0a4f363dc2f13d2f871f917cc563796d9ddc78d1" + "reference": "3dcd47d4bbd9fea4d1210e7a7a0a5ca02d99df14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/0a4f363dc2f13d2f871f917cc563796d9ddc78d1", - "reference": "0a4f363dc2f13d2f871f917cc563796d9ddc78d1", + "url": "https://api.github.com/repos/symfony/config/zipball/3dcd47d4bbd9fea4d1210e7a7a0a5ca02d99df14", + "reference": "3dcd47d4bbd9fea4d1210e7a7a0a5ca02d99df14", "shasum": "" }, "require": { @@ -547,7 +547,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v5.4.36" + "source": "https://github.com/symfony/config/tree/v5.4.38" }, "funding": [ { @@ -563,7 +563,7 @@ "type": "tidelift" } ], - "time": "2024-02-23T16:13:23+00:00" + "time": "2024-03-22T10:04:40+00:00" }, { "name": "symfony/console", @@ -666,16 +666,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v5.4.36", + "version": "v5.4.38", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "cc1fb237cd0e6da33005062b13b8485deb6e4440" + "reference": "0ba1fa459d284a9398c71afa1cb5d13de025de17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/cc1fb237cd0e6da33005062b13b8485deb6e4440", - "reference": "cc1fb237cd0e6da33005062b13b8485deb6e4440", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/0ba1fa459d284a9398c71afa1cb5d13de025de17", + "reference": "0ba1fa459d284a9398c71afa1cb5d13de025de17", "shasum": "" }, "require": { @@ -735,7 +735,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.36" + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.38" }, "funding": [ { @@ -751,7 +751,7 @@ "type": "tidelift" } ], - "time": "2024-02-22T18:43:31+00:00" + "time": "2024-03-18T16:56:51+00:00" }, { "name": "symfony/deprecation-contracts", @@ -976,16 +976,16 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.4.0", + "version": "v3.4.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + "reference": "4e64b49bf370ade88e567de29465762e316e4224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", - "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/4e64b49bf370ade88e567de29465762e316e4224", + "reference": "4e64b49bf370ade88e567de29465762e316e4224", "shasum": "" }, "require": { @@ -1032,7 +1032,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.2" }, "funding": [ { @@ -1048,20 +1048,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-01-23T14:51:35+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.35", + "version": "v5.4.38", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "5a553607d4ffbfa9c0ab62facadea296c9db7086" + "reference": "899330a01056077271e2f614c7b28b0379a671eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/5a553607d4ffbfa9c0ab62facadea296c9db7086", - "reference": "5a553607d4ffbfa9c0ab62facadea296c9db7086", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/899330a01056077271e2f614c7b28b0379a671eb", + "reference": "899330a01056077271e2f614c7b28b0379a671eb", "shasum": "" }, "require": { @@ -1096,7 +1096,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.35" + "source": "https://github.com/symfony/filesystem/tree/v5.4.38" }, "funding": [ { @@ -1112,7 +1112,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T13:51:25+00:00" + "time": "2024-03-21T08:05:07+00:00" }, { "name": "symfony/finder", @@ -1256,16 +1256,16 @@ }, { "name": "symfony/http-kernel", - "version": "v5.4.37", + "version": "v5.4.38", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "4ef7ed872564852b3c6c15fecf492975a52cbff3" + "reference": "21c32c7c6c32ea8d0f4b8e88a2607a2dc72799e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4ef7ed872564852b3c6c15fecf492975a52cbff3", - "reference": "4ef7ed872564852b3c6c15fecf492975a52cbff3", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/21c32c7c6c32ea8d0f4b8e88a2607a2dc72799e5", + "reference": "21c32c7c6c32ea8d0f4b8e88a2607a2dc72799e5", "shasum": "" }, "require": { @@ -1348,7 +1348,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.37" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.38" }, "funding": [ { @@ -1364,7 +1364,7 @@ "type": "tidelift" } ], - "time": "2024-03-04T20:55:44+00:00" + "time": "2024-04-02T19:56:39+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1825,16 +1825,16 @@ }, { "name": "symfony/service-contracts", - "version": "v2.5.2", + "version": "v2.5.3", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", "shasum": "" }, "require": { @@ -1888,7 +1888,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/service-contracts/tree/v2.5.3" }, "funding": [ { @@ -1904,7 +1904,7 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:29+00:00" + "time": "2023-04-21T15:04:16+00:00" }, { "name": "symfony/string", @@ -1994,16 +1994,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.4.4", + "version": "v6.4.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "b439823f04c98b84d4366c79507e9da6230944b1" + "reference": "95bd2706a97fb875185b51ecaa6112ec184233d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b439823f04c98b84d4366c79507e9da6230944b1", - "reference": "b439823f04c98b84d4366c79507e9da6230944b1", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/95bd2706a97fb875185b51ecaa6112ec184233d4", + "reference": "95bd2706a97fb875185b51ecaa6112ec184233d4", "shasum": "" }, "require": { @@ -2059,7 +2059,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.4" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.6" }, "funding": [ { @@ -2075,7 +2075,7 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:23:52+00:00" + "time": "2024-03-19T11:56:30+00:00" }, { "name": "symfony/yaml",