Cancel scheduled progress runs when executed manually
[MAILPOET-4977]
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace MailPoet\Automation\Engine\Control;
|
||||
|
||||
use ActionScheduler_Action;
|
||||
|
||||
class ActionScheduler {
|
||||
private const GROUP_ID = 'mailpoet-automation';
|
||||
|
||||
@@ -16,4 +18,13 @@ class ActionScheduler {
|
||||
public function hasScheduledAction(string $hook, array $args = []): bool {
|
||||
return as_has_scheduled_action($hook, $args, self::GROUP_ID);
|
||||
}
|
||||
|
||||
/** @return ActionScheduler_Action[] */
|
||||
public function getScheduledActions(array $args = []): array {
|
||||
return as_get_scheduled_actions(array_merge($args, ['group' => self::GROUP_ID]));
|
||||
}
|
||||
|
||||
public function unscheduleAction(string $hook, array $args = []): ?int {
|
||||
return as_unschedule_action($hook, $args, self::GROUP_ID);
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace MailPoet\Automation\Engine\Control;
|
||||
|
||||
use ActionScheduler_CanceledAction;
|
||||
use MailPoet\Automation\Engine\Data\AutomationRunLog;
|
||||
use MailPoet\Automation\Engine\Exceptions;
|
||||
use MailPoet\Automation\Engine\Hooks;
|
||||
@@ -35,6 +36,19 @@ class AutomationController {
|
||||
'step_id' => $stepId,
|
||||
'run_number' => $runNumber,
|
||||
];
|
||||
|
||||
// if a pending action exists, unschedule it
|
||||
$this->actionScheduler->unscheduleAction(Hooks::AUTOMATION_STEP, [$args]);
|
||||
|
||||
// if an action still exists (pending, in-progress, complete, failed), it's an error
|
||||
$actions = $this->actionScheduler->getScheduledActions(['hook' => Hooks::AUTOMATION_STEP, 'args' => [$args]]);
|
||||
$processedActions = array_filter($actions, function ($action) {
|
||||
return !$action instanceof ActionScheduler_CanceledAction;
|
||||
});
|
||||
if (count($processedActions) > 0) {
|
||||
throw Exceptions::stepActionProcessed($stepId, $runId, $runNumber);
|
||||
}
|
||||
|
||||
$this->actionScheduler->enqueue(Hooks::AUTOMATION_STEP, [$args]);
|
||||
}
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@ class Exceptions {
|
||||
private const AUTOMATION_HAS_ACTIVE_RUNS = 'mailpoet_automation_has_active_runs';
|
||||
private const AUTOMATION_STEP_NOT_STARTED = 'mailpoet_automation_step_not_started';
|
||||
private const AUTOMATION_STEP_NOT_RUNNING = 'mailpoet_automation_step_not_running';
|
||||
private const AUTOMATION_STEP_ACTION_PROCESSED = 'mailpoet_automation_step_action_processed';
|
||||
|
||||
public function __construct() {
|
||||
throw new InvalidStateException(
|
||||
@@ -290,4 +291,11 @@ class Exceptions {
|
||||
// translators: %1$s is the ID of the automation step, %2$s its current status, %3$d is the automation run ID.
|
||||
->withMessage(sprintf(__("Automation step with ID '%1\$s' is not running in automation run with ID '%2\$d'. Status: '%3\$s'", 'mailpoet'), $id, $runId, $status));
|
||||
}
|
||||
|
||||
public static function stepActionProcessed(string $id, int $runId, int $runNumber): InvalidStateException {
|
||||
return InvalidStateException::create()
|
||||
->withErrorCode(self::AUTOMATION_STEP_ACTION_PROCESSED)
|
||||
// translators: %1$d is the automation run ID, %2$s is the ID of the automation step, %3$d is the run number.
|
||||
->withMessage(sprintf(__("Automation run with ID '%1\$d' already has a processed action for step with ID '%2\$s' and run number '%3\$d'.", 'mailpoet'), $runId, $id, $runNumber));
|
||||
}
|
||||
}
|
||||
|
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace MailPoet\Test\Automation\Engine\Control;
|
||||
|
||||
use ActionScheduler;
|
||||
use ActionScheduler_NullSchedule;
|
||||
use ActionScheduler_Store;
|
||||
use MailPoet\Automation\Engine\Control\ActionScheduler as AutomationActionScheduler;
|
||||
use MailPoet\Automation\Engine\Control\AutomationController;
|
||||
use MailPoet\Automation\Engine\Data\Automation;
|
||||
use MailPoet\Automation\Engine\Data\AutomationRun;
|
||||
@@ -60,6 +62,40 @@ class AutomationControllerTest extends MailPoetTest {
|
||||
$controller->enqueueProgress(1, 'abc');
|
||||
}
|
||||
|
||||
public function testItUnschedulesPendingAction(): void {
|
||||
$this->createAutomationWithStepRunAndLog(AutomationRun::STATUS_RUNNING, AutomationRunLog::STATUS_RUNNING);
|
||||
|
||||
$data = ['automation_run_id' => 1, 'step_id' => 'abc', 'run_number' => 2];
|
||||
$this->scheduleAction(time() + MONTH_IN_SECONDS, $data);
|
||||
$this->assertCount(1, $this->getActions());
|
||||
$this->assertCount(1, $this->getActions(['status' => [ActionScheduler_Store::STATUS_PENDING]]));
|
||||
|
||||
$controller = $this->getServiceWithOverrides(AutomationController::class, [
|
||||
// skip creating new action so we can check if the existing one is unscheduled
|
||||
'actionScheduler' => $this->make(AutomationActionScheduler::class, ['enqueue' => 123]),
|
||||
]);
|
||||
$controller->enqueueProgress(1, 'abc');
|
||||
|
||||
$this->assertCount(1, $this->getActions());
|
||||
$this->assertCount(1, $this->getActions(['status' => [ActionScheduler_Store::STATUS_CANCELED]]));
|
||||
}
|
||||
|
||||
public function testItFailsWithExistingAction(): void {
|
||||
$this->createAutomationWithStepRunAndLog(AutomationRun::STATUS_RUNNING, AutomationRunLog::STATUS_RUNNING);
|
||||
|
||||
$data = ['automation_run_id' => 1, 'step_id' => 'abc', 'run_number' => 2];
|
||||
$actionId = $this->scheduleAction(time() + MONTH_IN_SECONDS, $data);
|
||||
ActionScheduler::store()->mark_complete($actionId);
|
||||
$this->assertCount(1, $this->getActions());
|
||||
$this->assertCount(1, $this->getActions(['status' => [ActionScheduler_Store::STATUS_COMPLETE]]));
|
||||
|
||||
$this->expectException(InvalidStateException::class);
|
||||
$this->expectExceptionMessage("Automation run with ID '1' already has a processed action for step with ID 'abc' and run number '2'.");
|
||||
|
||||
$controller = $this->diContainer->get(AutomationController::class);
|
||||
$controller->enqueueProgress(1, 'abc');
|
||||
}
|
||||
|
||||
private function createAutomationWithStepRunAndLog(string $runStatus, string $logStatus): void {
|
||||
$step = new Step('abc', Step::TYPE_ACTION, 'key', [], []);
|
||||
$automation = (new DataFactories\Automation())
|
||||
@@ -78,6 +114,10 @@ class AutomationControllerTest extends MailPoetTest {
|
||||
->create();
|
||||
}
|
||||
|
||||
private function scheduleAction(int $timestamp, array $args): int {
|
||||
return as_schedule_single_action($timestamp, Hooks::AUTOMATION_STEP, [$args], 'mailpoet-automation');
|
||||
}
|
||||
|
||||
private function getActions(array $args = []): array {
|
||||
return array_values(
|
||||
as_get_scheduled_actions(
|
||||
|
Reference in New Issue
Block a user