-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Torr\TaskManager\Entity; | ||
|
||
use Doctrine\Common\Collections\ArrayCollection; | ||
use Doctrine\Common\Collections\Collection; | ||
use Doctrine\DBAL\Types\Types; | ||
use Doctrine\ORM\Mapping as ORM; | ||
use function Symfony\Component\Clock\now; | ||
use Torr\TaskManager\Exception\Log\InvalidLogActionException; | ||
|
||
/** | ||
* @final | ||
*/ | ||
#[ORM\Entity] | ||
#[ORM\Table(name: "task_manager_tasks")] | ||
class TaskLog | ||
{ | ||
/** | ||
*/ | ||
#[ORM\Id] | ||
#[ORM\GeneratedValue(strategy: "AUTO")] | ||
#[ORM\Column(name: "id", type: Types::INTEGER)] | ||
private ?int $id = null; | ||
|
||
/** | ||
* ULIDs have only 22 characters, but just to be sure | ||
*/ | ||
#[ORM\Column(type: Types::STRING, length: 50, unique: true)] | ||
private string $taskId; | ||
|
||
/** | ||
* | ||
*/ | ||
#[ORM\Column(name: "time_queued", type: Types::DATETIMETZ_IMMUTABLE)] | ||
private \DateTimeImmutable $timeQueued; | ||
|
||
/** @var Collection<int, TaskRun> */ | ||
#[ORM\OneToMany(mappedBy: "taskLog", targetEntity: TaskRun::class)] | ||
#[ORM\OrderBy(["timeStarted" => "asc"])] | ||
private Collection $runs; | ||
|
||
|
||
public function __construct ( | ||
string $taskId, | ||
) | ||
{ | ||
$this->taskId = $taskId; | ||
$this->runs = new ArrayCollection(); | ||
$this->timeQueued = now(); | ||
} | ||
|
||
/** | ||
*/ | ||
public function getId () : ?int | ||
{ | ||
return $this->id; | ||
} | ||
|
||
/** | ||
*/ | ||
public function getTaskId () : string | ||
{ | ||
return $this->taskId; | ||
} | ||
|
||
/** | ||
*/ | ||
public function getTimeQueued () : \DateTimeImmutable | ||
{ | ||
return $this->timeQueued; | ||
} | ||
|
||
|
||
/** | ||
* @return Collection<int, TaskRun> | ||
*/ | ||
public function getRuns () : Collection | ||
{ | ||
return $this->runs; | ||
} | ||
|
||
|
||
/** | ||
*/ | ||
public function isFinishedSuccessfully () : ?bool | ||
{ | ||
foreach ($this->runs as $run) | ||
{ | ||
if ($run->isFinishedSuccessfully()) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
*/ | ||
public function getLastUnfinishedRun () : ?TaskRun | ||
{ | ||
foreach ($this->runs as $run) | ||
{ | ||
if (!$run->isFinished()) | ||
{ | ||
return $run; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* | ||
*/ | ||
public function startRun () : TaskRun | ||
{ | ||
if ($this->isFinishedSuccessfully()) | ||
{ | ||
throw new InvalidLogActionException("Can't start a run for a task #{$this->id} that is already finished."); | ||
} | ||
|
||
$run = new TaskRun($this); | ||
$this->runs->add($run); | ||
return $run; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Torr\TaskManager\Entity; | ||
|
||
use Doctrine\DBAL\Types\Types; | ||
use Doctrine\ORM\Mapping as ORM; | ||
use function Symfony\Component\Clock\now; | ||
use Torr\TaskManager\Exception\Log\InvalidLogActionException; | ||
|
||
/** | ||
* @final | ||
*/ | ||
#[ORM\Entity] | ||
#[ORM\Table(name: "task_manager_runs")] | ||
class TaskRun | ||
{ | ||
//region Fields | ||
/** | ||
*/ | ||
#[ORM\Id] | ||
#[ORM\GeneratedValue(strategy: "AUTO")] | ||
#[ORM\Column(name: "id", type: Types::INTEGER)] | ||
private ?int $id = null; | ||
|
||
/** | ||
*/ | ||
#[ORM\ManyToOne(targetEntity: TaskLog::class, inversedBy: "runs")] | ||
private TaskLog $taskLog; | ||
|
||
/** | ||
* | ||
*/ | ||
#[ORM\Column(name: "time_started", type: Types::DATETIMETZ_IMMUTABLE)] | ||
private \DateTimeImmutable $timeStarted; | ||
|
||
/** | ||
* | ||
*/ | ||
#[ORM\Column(name: "time_finished", type: Types::DATETIMETZ_IMMUTABLE, nullable: true)] | ||
private ?\DateTimeImmutable $timeFinished = null; | ||
|
||
/** | ||
*/ | ||
#[ORM\Column(type: Types::BOOLEAN, nullable: true)] | ||
private ?bool $successful = null; | ||
|
||
/** | ||
*/ | ||
#[ORM\Column(type: Types::BOOLEAN, nullable: true)] | ||
private ?bool $finishedProperly = null; | ||
|
||
/** | ||
*/ | ||
#[ORM\Column(type: Types::TEXT, nullable: true)] | ||
private ?string $output = null; | ||
//endregion | ||
|
||
|
||
/** | ||
*/ | ||
public function __construct (TaskLog $taskLog) | ||
{ | ||
$this->taskLog = $taskLog; | ||
$this->timeStarted = now(); | ||
} | ||
|
||
|
||
//region Accessors | ||
/** | ||
*/ | ||
public function getTaskLog () : TaskLog | ||
{ | ||
return $this->taskLog; | ||
} | ||
|
||
/** | ||
*/ | ||
public function getTimeStarted () : \DateTimeImmutable | ||
{ | ||
return $this->timeStarted; | ||
} | ||
|
||
/** | ||
*/ | ||
public function getTimeFinished () : ?\DateTimeImmutable | ||
{ | ||
return $this->timeFinished; | ||
} | ||
|
||
/** | ||
* Whether the task was finished successfully. | ||
*/ | ||
public function isFinishedSuccessfully () : bool | ||
{ | ||
return true === $this->successful; | ||
} | ||
|
||
/** | ||
*/ | ||
public function getOutput () : ?string | ||
{ | ||
return $this->output; | ||
} | ||
|
||
/** | ||
*/ | ||
public function isFinished () : bool | ||
{ | ||
return null !== $this->timeFinished; | ||
} | ||
|
||
/** | ||
* Whether the task was finished properly or was automatically finished. | ||
*/ | ||
public function hasFinishedProperly () : bool | ||
{ | ||
return true === $this->finishedProperly; | ||
} | ||
//endregion | ||
|
||
|
||
/** | ||
*/ | ||
public function finish (bool $successful, ?string $output) : void | ||
{ | ||
if ($this->isFinished()) | ||
{ | ||
throw new InvalidLogActionException("Can't finish task run #{$this->id} as it is already finished."); | ||
} | ||
|
||
$this->finishedProperly = true; | ||
$this->successful = $successful; | ||
$this->output = $output; | ||
$this->timeFinished = now(); | ||
} | ||
|
||
|
||
/** | ||
* Aborts the task, without finishing it properly | ||
*/ | ||
public function abort (bool $successful, ?string $output = null) : void | ||
{ | ||
if ($this->isFinished()) | ||
{ | ||
throw new InvalidLogActionException("Can't abort task run #{$this->id} as it is already finished."); | ||
} | ||
|
||
$this->finishedProperly = false; | ||
$this->successful = $successful; | ||
$this->output = $output; | ||
$this->timeFinished = now(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Torr\TaskManager\Exception\Log; | ||
|
||
use Torr\TaskManager\Exception\TaskManagerException; | ||
|
||
final class InvalidLogActionException extends \RuntimeException implements TaskManagerException | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Torr\TaskManager\Model; | ||
|
||
use Doctrine\ORM\EntityManagerInterface; | ||
use Doctrine\ORM\EntityRepository; | ||
use Torr\TaskManager\Entity\TaskLog; | ||
use Torr\TaskManager\Entity\TaskRun; | ||
use Torr\TaskManager\Task\Task; | ||
|
||
final class TaskLogModel | ||
{ | ||
/** @var EntityRepository<TaskLog> */ | ||
private EntityRepository $repository; | ||
|
||
/** | ||
*/ | ||
public function __construct ( | ||
private readonly EntityManagerInterface $entityManager, | ||
) | ||
{ | ||
$repository = $this->entityManager->getRepository(TaskLog::class); | ||
\assert($repository instanceof EntityRepository); | ||
$this->repository = $repository; | ||
} | ||
|
||
|
||
/** | ||
* Gets or creates the log entry for the given task | ||
*/ | ||
public function getLogForTask (Task $task) : TaskLog | ||
{ | ||
$log = $this->repository->findOneBy([ | ||
"taskId" => $task->ulid, | ||
]); | ||
|
||
if (null !== $log) | ||
{ | ||
return $log; | ||
} | ||
|
||
// if it isn't created yet, create a new one | ||
$log = new TaskLog($task->ulid); | ||
$this->entityManager->persist($log); | ||
|
||
return $log; | ||
} | ||
|
||
|
||
/** | ||
* Creates a new run for the given task (lok) and marks it as persisted. | ||
*/ | ||
public function createRunForTask (TaskLog $log) : TaskRun | ||
{ | ||
$run = new TaskRun($log); | ||
$this->entityManager->persist($run); | ||
|
||
return $run; | ||
} | ||
|
||
|
||
/** | ||
*/ | ||
public function flush () : void | ||
{ | ||
$this->entityManager->flush(); | ||
} | ||
} |