Skip to content

Commit

Permalink
Resolved code review feedback, added commands, completed message hand…
Browse files Browse the repository at this point in the history
…ler.
  • Loading branch information
DumitracheAdrian committed Apr 16, 2024
1 parent 663a50c commit 18d6734
Show file tree
Hide file tree
Showing 12 changed files with 337 additions and 5 deletions.
56 changes: 56 additions & 0 deletions packages/cron-job/Command/QueueCronJobByNameCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Draw\Component\CronJob\Command;

use Draw\Component\CronJob\CronJobProcessor;
use Draw\Component\CronJob\Entity\CronJob;
use Symfony\Bridge\Doctrine\ManagerRegistry;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'draw:cron-job:queue-by-name')]
class QueueCronJobByNameCommand extends Command
{
public function __construct(
private ManagerRegistry $managerRegistry,
private CronJobProcessor $cronJobProcessor,
) {
parent::__construct();
}

protected function configure(): void
{
$this
->setDescription('Queues cron job by name')
->addArgument('name', InputArgument::REQUIRED, 'Cron job name');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$cronJob = $this->managerRegistry
->getRepository(CronJob::class)
->findOneBy(['name' => $input->getArgument('name')]);

if (null === $cronJob) {
$io->error('Cron job could not be found.');

return Command::FAILURE;
}

$io->section('Queueing cron job...');

$this->cronJobProcessor->queue($cronJob, true);

$io->section('Cron job successfully queued.');

return Command::SUCCESS;
}
}
61 changes: 61 additions & 0 deletions packages/cron-job/Command/QueueDueCronJobsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Draw\Component\CronJob\Command;

use Doctrine\Persistence\ManagerRegistry;
use Draw\Component\CronJob\CronJobProcessor;
use Draw\Component\CronJob\Entity\CronJob;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'draw:cron-job:queue-due')]
class QueueDueCronJobsCommand extends Command
{
public function __construct(
private ManagerRegistry $managerRegistry,
private CronJobProcessor $cronJobProcessor,
) {
parent::__construct();
}

protected function configure(): void
{
$this
->setDescription('Queues due cron jobs');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->section('Queueing cron jobs...');

$cronJobs = array_map(
static fn(CronJob $cronJob): bool => $cronJob->isDue(),
$this->managerRegistry
->getRepository(CronJob::class)
->findAll()
);

$progress = $io->createProgressBar(\count($cronJobs));
$progress->setFormat(ProgressBar::FORMAT_DEBUG);

foreach ($cronJobs as $cronJob) {
$this->cronJobProcessor->queue($cronJob);

Check failure on line 49 in packages/cron-job/Command/QueueDueCronJobsCommand.php

View workflow job for this annotation

GitHub Actions / PHPStan

Parameter #1 $cronJob of method Draw\Component\CronJob\CronJobProcessor::queue() expects Draw\Component\CronJob\Entity\CronJob, bool given.

$progress->advance();
}

$progress->finish();

$io->newLine(2);
$io->success('Cron jobs successfully queued...');

return Command::SUCCESS;
}
}
19 changes: 19 additions & 0 deletions packages/cron-job/CronJobExecutionFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Draw\Component\CronJob;

use Draw\Component\CronJob\Entity\CronJob;
use Draw\Component\CronJob\Entity\CronJobExecution;

class CronJobExecutionFactory
{
public function create(CronJob $cronJob, bool $force = false): CronJobExecution
{
return (new CronJobExecution())
->setCronJob($cronJob)
->setRequestedAt(new \DateTimeImmutable())
->setForce($force);
}
}
41 changes: 39 additions & 2 deletions packages/cron-job/CronJobProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,52 @@

namespace Draw\Component\CronJob;

use Doctrine\Persistence\ManagerRegistry;
use Draw\Component\CronJob\Entity\CronJob;
use Draw\Component\CronJob\Entity\CronJobExecution;
use Draw\Component\CronJob\Message\ExecuteCronJobMessage;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Process\Process;

class CronJobProcessor
{
public function __construct()
public function __construct(
private CronJobExecutionFactory $cronJobExecutionFactory,
private ManagerRegistry $managerRegistry,
private MessageBusInterface $messageBus,
) {
}

public function queue(CronJob $cronJob, bool $force = false): void
{
$manager = $this->managerRegistry->getManagerForClass(CronJobExecution::class);

$manager->persist($execution = $this->cronJobExecutionFactory->create($cronJob, $force));
$manager->flush();

$this->messageBus->dispatch(new ExecuteCronJobMessage($execution));
}

public function process(CronJob $cronJob): void
public function process(CronJobExecution $execution): void
{
$manager = $this->managerRegistry->getManagerForClass(CronJobExecution::class);

$execution->start();
$manager->flush();

$command = $execution->getCronJob()->getCommand();

$process = Process::fromShellCommandline($command)
->setTimeout(1800);

try {
$process->run();

$execution->end($process->getExitCode());
} catch (\Throwable $error) {
$execution->fail($process->getExitCode(), ['TODO']);
} finally {
$manager->flush();
}
}
}
5 changes: 5 additions & 0 deletions packages/cron-job/Entity/CronJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ public function removeExecution(CronJobExecution $execution): self
return $this;
}

public function isDue(): bool
{
return false;
}

public function __toString(): string
{
return $this->name;
Expand Down
22 changes: 22 additions & 0 deletions packages/cron-job/Entity/CronJobExecution.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,26 @@ public function setCronJob(?CronJob $cronJob): self

return $this;
}

public function start(): void
{
$this
->setExecutionStartedAt(new \DateTimeImmutable())
->setExecutionEndedAt(null);
}

public function end(?int $exitCode): void
{
$this
->setExitCode($exitCode)
->setExecutionEndedAt($executionEndedAt = new \DateTimeImmutable())
->setExecutionDelay($executionEndedAt->getTimestamp() - $this->getExecutionStartedAt()->getTimestamp());
}

public function fail(?int $exitCode, ?array $error): void
{
$this
->setExitCode($exitCode)
->setError($error);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Draw\Component\CronJob\MessageHandler;

use Draw\Component\CronJob\CronJobProcessor;
use Draw\Component\CronJob\Event\PostCronJobExecutionEvent;
use Draw\Component\CronJob\Event\PreCronJobExecutionEvent;
use Draw\Component\CronJob\Message\ExecuteCronJobMessage;
Expand All @@ -14,6 +15,7 @@ class ExecuteCronJobMessageHandler
{
public function __construct(
private EventDispatcherInterface $eventDispatcher,
private CronJobProcessor $cronJobProcessor,
) {
}

Expand All @@ -28,7 +30,7 @@ public function handleExecuteCronJobMessage(ExecuteCronJobMessage $message): voi
return;
}

// @TODO
$this->cronJobProcessor->process($execution);

$this->eventDispatcher->dispatch(new PostCronJobExecutionEvent($execution));
}
Expand Down
6 changes: 4 additions & 2 deletions packages/cron-job/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
],
"require": {
"php": ">=8.1",
"doctrine/orm": "^3.1",
"doctrine/orm": "^2.11",
"draw/core": "^0.11",
"draw/messenger": "^0.11",
"symfony/console": "^6.4.0",
"symfony/event-dispatcher": "^6.4.0",
"symfony/messenger": "^6.4.0"
"symfony/messenger": "^6.4.0",
"symfony/process": "^6.4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0 || ^10.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Draw\Bundle\FrameworkExtraBundle\Tests\DependencyInjection\Integration;

use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\CronJobIntegration;
use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\IntegrationInterface;
use Draw\Component\CronJob\CronJobProcessor;
use Draw\Component\CronJob\MessageHandler\ExecuteCronJobMessageHandler;
use PHPUnit\Framework\Attributes\CoversClass;

#[CoversClass(CronJobIntegration::class)]
class CronJobIntegrationTest extends IntegrationTestCase
{
public function createIntegration(): IntegrationInterface
{
return new CronJobIntegration();
}

public function getConfigurationSectionName(): string
{
return 'cron_job';
}

public function getDefaultConfiguration(): array
{
return [];
}

public static function provideTestLoad(): iterable
{
yield [
[],
[
new ServiceConfiguration(
'draw.cron_job.cron_job_processor',
[
CronJobProcessor::class,
]
),
new ServiceConfiguration(
'draw.cron_job.message_handler.execute_cron_job_message_handler',
[
ExecuteCronJobMessageHandler::class,
]
)
],
[],
];
}
}
1 change: 1 addition & 0 deletions packages/framework-extra-bundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"draw/aws-tool-kit": "^0.11",
"draw/console": "^0.11",
"draw/contracts": "^0.11",
"draw/cron-job": "^0.11",
"draw/doctrine-extra": "^0.11",
"draw/entity-migrator": "^0.11",
"draw/log": "^0.11",
Expand Down
63 changes: 63 additions & 0 deletions packages/sonata-integration-bundle/CronJob/Admin/CronJobAdmin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Draw\Bundle\SonataIntegrationBundle\CronJob\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;

class CronJobAdmin extends AbstractAdmin
{
protected function configureDatagridFilters(DatagridMapper $filter): void
{
$filter
->add('id')
->add('name')
->add('command')
->add('active');

}

protected function configureListFields(ListMapper $list): void
{
$list
->addIdentifier('name')
->add('command')
->add('schedule')
->add('active', null, ['editable' => true])
->add('timeToLive')
->add('priority');
}

protected function configureFormFields(FormMapper $form): void
{
$form
->add('name')
->add(
'command',
null,
[
'help' => 'Enter the full command to run excluding stdOut and stdErr directive (... 2>&1 | logger -t ...)<p>Parameters bag is available. Use like %kernel.project_dir%</p>',
]
)
->add('schedule')
->add('active')
->add('timeToLive')
->add('priority');
}

protected function configureShowFields(ShowMapper $show): void
{
$show
->add('name')
->add('command')
->add('schedule')
->add('active', null, ['editable' => true])
->add('timeToLive')
->add('priority');
}
}
Loading

0 comments on commit 18d6734

Please sign in to comment.