mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-28 21:16:13 +00:00
Suffix message class with 'Message' and add check on workflow to assert no transitions were applied since message placed in queue
This commit is contained in:
parent
5d84e997c1
commit
cb446edd18
@ -54,8 +54,8 @@ class CancelStaleWorkflowCronJob implements CronJobInterface
|
|||||||
|
|
||||||
foreach ($staleWorkflowIds as $wId) {
|
foreach ($staleWorkflowIds as $wId) {
|
||||||
try {
|
try {
|
||||||
$this->messageBus->dispatch(new CancelStaleWorkflow($wId));
|
$this->messageBus->dispatch(new CancelStaleWorkflowMessage($wId));
|
||||||
$lastCanceled = $wId;
|
$lastCanceled = max($wId, $lastCanceled);
|
||||||
++$processedCount;
|
++$processedCount;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->logger->error("Failed to dispatch CancelStaleWorkflow for ID {$wId}", ['exception' => $e]);
|
$this->logger->error("Failed to dispatch CancelStaleWorkflow for ID {$wId}", ['exception' => $e]);
|
||||||
|
@ -14,6 +14,7 @@ namespace Chill\MainBundle\Service\Workflow;
|
|||||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Clock\ClockInterface;
|
||||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||||
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
||||||
use Symfony\Component\Workflow\Registry;
|
use Symfony\Component\Workflow\Registry;
|
||||||
@ -21,13 +22,29 @@ use Symfony\Component\Workflow\Registry;
|
|||||||
#[AsMessageHandler]
|
#[AsMessageHandler]
|
||||||
class CancelStaleWorkflowHandler
|
class CancelStaleWorkflowHandler
|
||||||
{
|
{
|
||||||
public function __construct(private readonly EntityWorkflowRepository $workflowRepository, private readonly Registry $registry, private EntityManagerInterface $em, private LoggerInterface $logger) {}
|
public const string KEEP_INTERVAL = 'P90D';
|
||||||
|
|
||||||
public function __invoke(CancelStaleWorkflow $message): void
|
public function __construct(private readonly EntityWorkflowRepository $workflowRepository, private readonly Registry $registry, private readonly EntityManagerInterface $em, private readonly LoggerInterface $logger, private readonly ClockInterface $clock) {}
|
||||||
|
|
||||||
|
public function __invoke(CancelStaleWorkflowMessage $message): void
|
||||||
{
|
{
|
||||||
$workflowId = $message->getWorkflowId();
|
$workflowId = $message->getWorkflowId();
|
||||||
|
|
||||||
|
$olderThanDate = $this->clock->now()->sub(new \DateInterval(self::KEEP_INTERVAL));
|
||||||
|
$staleWorkflows = $this->workflowRepository->findWorkflowsWithoutFinalStepAndOlderThan($olderThanDate);
|
||||||
|
|
||||||
$workflow = $this->workflowRepository->find($workflowId);
|
$workflow = $this->workflowRepository->find($workflowId);
|
||||||
|
|
||||||
|
if (in_array($workflow, $staleWorkflows, true)) {
|
||||||
|
$this->logger->alert('Workflow has transitioned in the meantime.', [$workflowId]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $workflow) {
|
||||||
|
$this->logger->alert('Workflow was not found!', [$workflowId]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$workflowComponent = $this->registry->get($workflow, $workflow->getWorkflowName());
|
$workflowComponent = $this->registry->get($workflow, $workflow->getWorkflowName());
|
||||||
$metadataStore = $workflowComponent->getMetadataStore();
|
$metadataStore = $workflowComponent->getMetadataStore();
|
||||||
$transitions = $workflowComponent->getEnabledTransitions($workflow);
|
$transitions = $workflowComponent->getEnabledTransitions($workflow);
|
||||||
@ -44,15 +61,15 @@ class CancelStaleWorkflowHandler
|
|||||||
$isFinalPositive = $metadataStore->getMetadata('isFinalPositive', $transition);
|
$isFinalPositive = $metadataStore->getMetadata('isFinalPositive', $transition);
|
||||||
|
|
||||||
if ($isFinal && !$isFinalPositive) {
|
if ($isFinal && !$isFinalPositive) {
|
||||||
$workflowComponent->apply($workflow, 'annule');
|
$workflowComponent->apply($workflow, $transition->getName());
|
||||||
$this->logger->info(sprintf('EntityWorkflow %d has been transitioned.', $workflowId));
|
$this->logger->info('EntityWorkflow has been cancelled automatically.', [$workflowId]);
|
||||||
$transitionApplied = true;
|
$transitionApplied = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$transitionApplied) {
|
if (!$transitionApplied) {
|
||||||
$this->logger->error(sprintf('No valid transition found for EntityWorkflow %d.', $workflowId));
|
$this->logger->error('No valid transition found for EntityWorkflow %d.', [$workflowId]);
|
||||||
throw new UnrecoverableMessageHandlingException(sprintf('No valid transition found for EntityWorkflow %d.', $workflowId));
|
throw new UnrecoverableMessageHandlingException(sprintf('No valid transition found for EntityWorkflow %d.', $workflowId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Service\Workflow;
|
namespace Chill\MainBundle\Service\Workflow;
|
||||||
|
|
||||||
class CancelStaleWorkflow
|
class CancelStaleWorkflowMessage
|
||||||
{
|
{
|
||||||
public function __construct(public int $workflowId) {}
|
public function __construct(public int $workflowId) {}
|
||||||
|
|
@ -15,6 +15,10 @@ use Chill\MainBundle\Entity\CronJobExecution;
|
|||||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
||||||
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflow;
|
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflow;
|
||||||
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflowCronJob;
|
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflowCronJob;
|
||||||
|
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflowMessage;
|
||||||
|
use DateInvalidTimeZoneException;
|
||||||
|
use DateMalformedStringException;
|
||||||
|
use DateTimeImmutable;
|
||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use PHPUnit\Framework\MockObject\Exception;
|
use PHPUnit\Framework\MockObject\Exception;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@ -43,7 +47,7 @@ class CancelStaleWorkflowCronJobTest extends KernelTestCase
|
|||||||
*/
|
*/
|
||||||
public function testCanRun(?CronJobExecution $cronJobExecution, bool $expected): void
|
public function testCanRun(?CronJobExecution $cronJobExecution, bool $expected): void
|
||||||
{
|
{
|
||||||
$clock = new MockClock(new \DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));
|
$clock = new MockClock(new DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));
|
||||||
$logger = $this->createMock(LoggerInterface::class);
|
$logger = $this->createMock(LoggerInterface::class);
|
||||||
|
|
||||||
$cronJob = new CancelStaleWorkflowCronJob($this->createMock(EntityWorkflowRepository::class), $clock, $this->buildMessageBus(), $logger);
|
$cronJob = new CancelStaleWorkflowCronJob($this->createMock(EntityWorkflowRepository::class), $clock, $this->buildMessageBus(), $logger);
|
||||||
@ -52,17 +56,17 @@ class CancelStaleWorkflowCronJobTest extends KernelTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \DateMalformedStringException
|
* @throws DateMalformedStringException
|
||||||
* @throws \DateInvalidTimeZoneException
|
* @throws DateInvalidTimeZoneException
|
||||||
* @throws Exception
|
* @throws \Exception|Exception
|
||||||
*/
|
*/
|
||||||
public function testRun(): void
|
public function testRun(): void
|
||||||
{
|
{
|
||||||
$clock = new MockClock((new \DateTimeImmutable('now', new \DateTimeZone('+00:00')))->add(new \DateInterval('P120D')));
|
$clock = new MockClock((new DateTimeImmutable('now', new \DateTimeZone('+00:00')))->add(new \DateInterval('P120D')));
|
||||||
$workflowRepository = $this->createMock(EntityWorkflowRepository::class);
|
$workflowRepository = $this->createMock(EntityWorkflowRepository::class);
|
||||||
$logger = $this->createMock(LoggerInterface::class);
|
$logger = $this->createMock(LoggerInterface::class);
|
||||||
|
|
||||||
$workflowRepository->method('findWorkflowsWithoutFinalStepAndOlderThan')->willReturn([1, 2, 3]);
|
$workflowRepository->method('findWorkflowsWithoutFinalStepAndOlderThan')->willReturn([1, 3, 2]);
|
||||||
$messageBus = $this->buildMessageBus(true);
|
$messageBus = $this->buildMessageBus(true);
|
||||||
|
|
||||||
$cronJob = new CancelStaleWorkflowCronJob($workflowRepository, $clock, $messageBus, $logger);
|
$cronJob = new CancelStaleWorkflowCronJob($workflowRepository, $clock, $messageBus, $logger);
|
||||||
@ -80,17 +84,17 @@ class CancelStaleWorkflowCronJobTest extends KernelTestCase
|
|||||||
public static function buildTestCanRunData(): iterable
|
public static function buildTestCanRunData(): iterable
|
||||||
{
|
{
|
||||||
yield [
|
yield [
|
||||||
(new CronJobExecution('last-canceled-workflow-id'))->setLastEnd(new \DateTimeImmutable('2023-12-31 00:00:00', new \DateTimeZone('+00:00'))),
|
(new CronJobExecution('last-canceled-workflow-id'))->setLastEnd(new DateTimeImmutable('2023-12-31 00:00:00', new \DateTimeZone('+00:00'))),
|
||||||
true,
|
true,
|
||||||
];
|
];
|
||||||
|
|
||||||
yield [
|
yield [
|
||||||
(new CronJobExecution('last-canceled-workflow-id'))->setLastEnd(new \DateTimeImmutable('2023-12-30 23:59:59', new \DateTimeZone('+00:00'))),
|
(new CronJobExecution('last-canceled-workflow-id'))->setLastEnd(new DateTimeImmutable('2023-12-30 23:59:59', new \DateTimeZone('+00:00'))),
|
||||||
true,
|
true,
|
||||||
];
|
];
|
||||||
|
|
||||||
yield [
|
yield [
|
||||||
(new CronJobExecution('last-canceled-workflow-id'))->setLastEnd(new \DateTimeImmutable('2023-12-31 00:00:01', new \DateTimeZone('+00:00'))),
|
(new CronJobExecution('last-canceled-workflow-id'))->setLastEnd(new DateTimeImmutable('2023-12-31 00:00:01', new \DateTimeZone('+00:00'))),
|
||||||
false,
|
false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -104,7 +108,7 @@ class CancelStaleWorkflowCronJobTest extends KernelTestCase
|
|||||||
false => $messageBus->method('dispatch'),
|
false => $messageBus->method('dispatch'),
|
||||||
};
|
};
|
||||||
|
|
||||||
$methodDispatch->willReturnCallback(function (CancelStaleWorkflow $message) {
|
$methodDispatch->willReturnCallback(function (CancelStaleWorkflowMessage $message) {
|
||||||
return new Envelope($message);
|
return new Envelope($message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
|||||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
||||||
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflow;
|
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflow;
|
||||||
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflowHandler;
|
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflowHandler;
|
||||||
|
use Chill\MainBundle\Service\Workflow\CancelStaleWorkflowMessage;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@ -51,7 +52,7 @@ class CancelStaleWorkflowHandlerTest extends TestCase
|
|||||||
|
|
||||||
$handler = new CancelStaleWorkflowHandler($workflowRepository, $registry, $em, $logger);
|
$handler = new CancelStaleWorkflowHandler($workflowRepository, $registry, $em, $logger);
|
||||||
|
|
||||||
$handler(new CancelStaleWorkflow(1));
|
$handler(new CancelStaleWorkflowMessage(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvokeWorkflowWithMultipleStepsAndValidTransition(): void
|
public function testInvokeWorkflowWithMultipleStepsAndValidTransition(): void
|
||||||
@ -92,7 +93,7 @@ class CancelStaleWorkflowHandlerTest extends TestCase
|
|||||||
|
|
||||||
$handler = new CancelStaleWorkflowHandler($workflowRepository, $registry, $em, $logger);
|
$handler = new CancelStaleWorkflowHandler($workflowRepository, $registry, $em, $logger);
|
||||||
|
|
||||||
$handler(new CancelStaleWorkflow(1));
|
$handler(new CancelStaleWorkflowMessage(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvokeWorkflowWithMultipleStepsAndNoValidTransition(): void
|
public function testInvokeWorkflowWithMultipleStepsAndNoValidTransition(): void
|
||||||
@ -134,6 +135,6 @@ class CancelStaleWorkflowHandlerTest extends TestCase
|
|||||||
|
|
||||||
$handler = new CancelStaleWorkflowHandler($workflowRepository, $registry, $em, $logger);
|
$handler = new CancelStaleWorkflowHandler($workflowRepository, $registry, $em, $logger);
|
||||||
|
|
||||||
$handler(new CancelStaleWorkflow(1));
|
$handler(new CancelStaleWorkflowMessage(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user