diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index d3b75dd..d0ed684 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -7,7 +7,6 @@ use Flowpack\Task\Domain\Repository\TaskExecutionRepository; use Flowpack\Task\Domain\Runner\TaskRunner; use Flowpack\Task\Domain\Scheduler\Scheduler; -use Flowpack\Task\Domain\Task\Task; use Flowpack\Task\Domain\Task\TaskCollectionFactory; use Flowpack\Task\Domain\Task\TaskExecutionHistory; use Flowpack\Task\Domain\Task\TaskInterface; @@ -154,6 +153,60 @@ public function showCommand(string $taskIdentifier): void ); } + /** + * Clean task execution history database. + * + * Removes specified entries from the task execution history database. + * + * Can filter by task, task status or date. + * + * @param string|null $task Task Identifier + * @param string|null $before Datetime + * @param string|null $status Status, use ',' to separate multiple + * @param bool $dry Enable dryrun, does not delete entries + * @param bool $verbose Enable Verbose output + */ + public function cleanCommand(?string $task = null, ?string $before = null, ?string $status = null, bool $verbose = false, bool $dry=false): void + { + $statusArray = []; + if ($status !== null) { + foreach (explode(',', $status) as $s) { + $statusArray[] = trim($s); + } + } + + $targets = $this->taskExecutionRepository->findByOptions($task, $before, $statusArray); + + $confirm = true; + if (!$dry) $confirm = $this->output->askConfirmation("Do you want to delete " . (count($targets)) . " entries? [Y/n]"); + + if ($confirm) { + if (!$dry) $this->taskExecutionRepository->removeEntries($targets); + + if ($verbose) { + $this->output->outputTable(array_map(function (TaskExecution $task) { + $label = ''; + try { + $label = $this->getTaskByIdentifier($task->getTaskIdentifier())->getLabel(); + } catch (StopCommandException $exception) {} + return [ + $task->getTaskIdentifier(), + $label, + $task->getHandlerClass(), + $task->getStatus(), + $task->getScheduleTime()->format('Y-m-d H:i:s'), + $task->getStatus()!==TaskStatus::PLANNED ? $task->getStartTime()->format('Y-m-d H:i:s') : 'null', + $task->getStatus()!==TaskStatus::PLANNED ? $task->getEndTime()->format('Y-m-d H:i:s') : 'null', + ]; + }, $targets), + ['Identifier', 'Label', 'Handler Class', 'Status', 'Scheduled Time', 'Start Time', 'End Time'] + ); + + } + $this->outputLine(($dry ? "Targets " : "Removed ") . count($targets) . " entries"); + } + } + /** * @param TaskInterface $task * @return string @@ -183,4 +236,4 @@ private function getTaskByIdentifier(string $taskIdentifier): TaskInterface $this->quit(1); } } -} +} \ No newline at end of file diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 8ab0fa3..13d35e5 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,6 +64,67 @@ public function removePlannedTask(Task $task): void } } + /** + * Queries task executions by parameters. + * + * @param string|null $taskIdentifier + * @param string|null $before + * @param array $statusArray + * @return array + */ + public function findByOptions(?string $taskIdentifier, ?string $before, array $statusArray): array + { + $query = $this->createQuery(); + + $constraints = []; + if ($taskIdentifier !== null) { + $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); + } + if ($before !== null) $constraints[] = $query->logicalOr( + $query->lessThan('endTime', $before), + $query->lessThan('scheduleTime', $before), + ); + if ($statusArray) { + $statusConstraints = []; + + foreach ($statusArray as $status) { + $statusConstraints[] = $query->equals('status', $status); + } + + if (count($statusConstraints) === 1) { + $constraints[] = $statusConstraints[0]; + } elseif (count($statusConstraints) > 1) { + $constraints[] = $query->logicalOr($statusConstraints); + } + } + + if ($constraints) { + $query->matching( + $query->logicalAnd( + $constraints + ) + ); + } + + return $query->execute()->toArray(); + } + + /** + * @param array $entries + * @return void + * @throws \RuntimeException + */ + public function removeEntries(array $entries): void + { + foreach ($entries as $entry) { + try { + $this->remove($entry); + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } + } + } + public function findLatestExecution(Task $task, int $limit = 5, int $offset = 0): QueryResultInterface { $query = $this->createQuery(); diff --git a/Readme.md b/Readme.md index 4acfd67..6423a2f 100644 --- a/Readme.md +++ b/Readme.md @@ -4,7 +4,7 @@ This package provides a simple to use task scheduler for Neos Flow. Tasks are configured via settings, recurring tasks can be configured using cron syntax. Detailed options configure the first and last executions as well as options for the class handling the task. -Scheduling and running tasks are decoupled: The `Scheduler` schedules tasks whcih the are executed by the `TaskRunner`. This architecture allows receiving and displaying metrics of already executed tasks. +Scheduling and running tasks are decoupled: The `Scheduler` schedules tasks which then are executed by the `TaskRunner`. This architecture allows receiving and displaying metrics of already executed tasks. Most of the architectural ideas behind the package are taken from [php-task](https://github.com/php-task/php-task), and reimplemented for Neos Flow. @@ -81,3 +81,9 @@ Show details about a specific task: ```bash ./flow task:show ``` + +Truncate task history database: + +```bash +./flow task:clean [--task=] [--status=] [--before=] [--dry] [--verbose] +```