Skip to content

Commit

Permalink
Merge pull request #50 from synolia/feature/add-command-to-clean-history
Browse files Browse the repository at this point in the history
Add command to clean scheduled commands history
  • Loading branch information
TheGrimmChester authored Jan 13, 2022
2 parents a13d62b + e9a6670 commit 2281a84
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ behat-configure: ## Configure Behat
(cd ${TEST_DIRECTORY} && sed -i "s#vendor/sylius/sylius/src/Sylius/Behat/Resources/config/suites.yml#vendor/${PLUGIN_NAME}/tests/Behat/Resources/suites.yml#g" behat.yml)
(cd ${TEST_DIRECTORY} && sed -i "s#vendor/sylius/sylius/features#vendor/${PLUGIN_NAME}/features#g" behat.yml)
(cd ${TEST_DIRECTORY} && sed -i "s#@cli#@javascript#g" behat.yml)
(cd ${TEST_DIRECTORY} && echo ' - { resource: "../vendor/${PLUGIN_NAME}/tests/Behat/Resources/services.yml" }' >> config/services_test.yaml)
(cd ${TEST_DIRECTORY} && sed -i '2i \ \ \ \ - { resource: "../vendor/${PLUGIN_NAME}/tests/Behat/Resources/services.yml\" }' config/services_test.yaml)

grumphp: ## Run GrumPHP
vendor/bin/grumphp run
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,32 @@ sylius_fixtures:
enabled: false
```
## Commands
### synolia:scheduler-run
Execute scheduled commands.
* options:
* --id (run only a specific scheduled command)
**Run all scheduled commands :** php bin/console synolia:scheduler-run
**Run one scheduled command :** php bin/console synolia:scheduler-run --id=5
### synolia:scheduler:purge-history
Purge scheduled command history greater than {X} days old.
* options:
* --all (purge everything)
* --days (number of days to keep)
* --state (array of schedule states)
* --dry-run
**Example to remove all finished and in error scheduled commands after 7 days :**
php bin/console synolia:scheduler:purge-history --state=finished --state=error --days=7
## Optional services
```yaml
services:
Expand Down
124 changes: 124 additions & 0 deletions src/Command/PurgeScheduledCommandCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

declare(strict_types=1);

namespace Synolia\SyliusSchedulerCommandPlugin\Command;

use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Synolia\SyliusSchedulerCommandPlugin\Entity\ScheduledCommandInterface;
use Synolia\SyliusSchedulerCommandPlugin\Enum\ScheduledCommandStateEnum;
use Synolia\SyliusSchedulerCommandPlugin\Repository\ScheduledCommandRepositoryInterface;

class PurgeScheduledCommandCommand extends Command
{
private const DEFAULT_PURGE_PERIODE_IN_DAYS = 3;

private const DEFAULT_STATE = ScheduledCommandStateEnum::FINISHED;

private const DEFAULT_BATCH = 100;

protected static $defaultName = 'synolia:scheduler:purge-history';

/** @var EntityManagerInterface */
private $entityManager;

/** @var ScheduledCommandRepositoryInterface */
private $scheduledCommandRepository;

/** @var LoggerInterface */
private $logger;

private SymfonyStyle $io;

public function __construct(
EntityManagerInterface $entityManager,
ScheduledCommandRepositoryInterface $scheduledCommandRepository,
LoggerInterface $logger,
?string $name = null
) {
parent::__construct($name);

$this->entityManager = $entityManager;
$this->scheduledCommandRepository = $scheduledCommandRepository;
$this->logger = $logger;
}

protected function configure(): void
{
$this
->setDescription('Purge scheduled command history greater than {X} days old.')
->addOption('all', 'p', InputOption::VALUE_NONE, 'Remove all schedules with specified state (default is finished).')
->addOption('days', 'd', InputOption::VALUE_OPTIONAL, '{X} days old', self::DEFAULT_PURGE_PERIODE_IN_DAYS)
->addOption('state', 's', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'State of scheduled history to be cleaned', [self::DEFAULT_STATE])
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Dry run')
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$this->io = new SymfonyStyle($input, $output);

$purgeAll = $input->getOption('all');
$daysOld = (int) $input->getOption('days');
$state = $input->getOption('state');
/** @var bool $dryRun */
$dryRun = $input->getOption('dry-run') ?? false;

/** @var ScheduledCommandInterface[] $schedules */
$schedules = $this->getScheduledHistory($purgeAll, $daysOld, $state);

$counter = 0;
foreach ($schedules as $schedule) {
$this->logger->info(\sprintf(
'Removed scheduled command "%s" (%d)',
$schedule->getName(),
$schedule->getId(),
));

if ($dryRun) {
continue;
}

$this->entityManager->remove($schedule);

if ($counter % self::DEFAULT_BATCH === 0) {
$this->entityManager->flush();
}
$counter++;
}

$this->entityManager->flush();

return 0;
}

private function getScheduledHistory(bool $purgeAll, int $daysOld, array $states): iterable
{
if ($purgeAll) {
$this->io->note(\sprintf(
'All schedules with states ["%s"] will be purged.',
\implode(',', $states),
));

return $this->scheduledCommandRepository->findAllHavingState($states);
}

$maxDate = new \DateTime();
$maxDate->modify(\sprintf('-%d days', $daysOld));

$this->io->note(\sprintf(
'Schedules with states ["%s"] lesser than %s days(s) (%s) will be purged.',
\implode(',', $states),
$daysOld,
$maxDate->format('Y-m-d')
));

return $this->scheduledCommandRepository->findAllSinceXDaysWithState($maxDate, $states);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

declare(strict_types=1);

namespace Synolia\SyliusSchedulerCommandPlugin\EventSubscriber;
namespace Synolia\SyliusSchedulerCommandPlugin\DoctrineEvent;

use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
use Synolia\SyliusSchedulerCommandPlugin\Entity\ScheduledCommandInterface;

class DeletedScheduledCommandEventSubscriber implements EventSubscriberInterface
class ScheduledCommandPostRemoveEvent implements EventSubscriber
{
/** @var string */
private $logsDir;
Expand All @@ -18,14 +19,16 @@ public function __construct(string $logsDir)
$this->logsDir = $logsDir;
}

public static function getSubscribedEvents(): array
public function getSubscribedEvents(): array
{
return ['synolia.scheduled_command.pre_delete' => ['deleteLogFile']];
return [
Events::postRemove,
];
}

public function deleteLogFile(ResourceControllerEvent $event): void
public function postRemove(LifecycleEventArgs $eventArgs): void
{
$scheduledCommand = $event->getSubject();
$scheduledCommand = $eventArgs->getEntity();

if (!$scheduledCommand instanceof ScheduledCommandInterface) {
return;
Expand Down
22 changes: 22 additions & 0 deletions src/Repository/ScheduledCommandRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,26 @@ public function findLastCreatedCommand(CommandInterface $command): ?ScheduledCom
return null;
}
}

public function findAllSinceXDaysWithState(\DateTimeInterface $dateTime, array $states): iterable
{
return $this->createQueryBuilder('scheduled')
->where('scheduled.state IN (:states)')
->andWhere('scheduled.createdAt < :createdAt')
->setParameter('states', $states)
->setParameter('createdAt', $dateTime->format('Y-m-d 00:00:00'))
->getQuery()
->getResult()
;
}

public function findAllHavingState(array $states): iterable
{
return $this->createQueryBuilder('scheduled')
->where('scheduled.state IN (:states)')
->setParameter('states', $states)
->getQuery()
->getResult()
;
}
}
4 changes: 4 additions & 0 deletions src/Repository/ScheduledCommandRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ interface ScheduledCommandRepositoryInterface extends RepositoryInterface
public function findAllRunnable(): iterable;

public function findLastCreatedCommand(CommandInterface $command): ?ScheduledCommandInterface;

public function findAllSinceXDaysWithState(\DateTimeInterface $dateTime, array $states): iterable;

public function findAllHavingState(array $states): iterable;
}
4 changes: 4 additions & 0 deletions src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@ services:
bind:
$pingInterval: '%env(int:SYNOLIA_SCHEDULER_PLUGIN_PING_INTERVAL)%'
$keepConnectionAlive: '%env(bool:SYNOLIA_SCHEDULER_PLUGIN_KEEP_ALIVE)%'

Synolia\SyliusSchedulerCommandPlugin\DoctrineEvent\ScheduledCommandPostRemoveEvent:
tags:
- { name: doctrine.event_subscriber, connection: default }
Loading

0 comments on commit 2281a84

Please # to comment.