Skip to content

Commit

Permalink
Add task log entities and model
Browse files Browse the repository at this point in the history
  • Loading branch information
apfelbox committed May 16, 2024
1 parent becfccc commit 5655998
Show file tree
Hide file tree
Showing 4 changed files with 358 additions and 0 deletions.
128 changes: 128 additions & 0 deletions src/Entity/TaskLog.php
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;
}
}
153 changes: 153 additions & 0 deletions src/Entity/TaskRun.php
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();
}
}
9 changes: 9 additions & 0 deletions src/Exception/Log/InvalidLogActionException.php
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
{
}
68 changes: 68 additions & 0 deletions src/Model/TaskLogModel.php
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();
}
}

0 comments on commit 5655998

Please sign in to comment.