149 lines
4.4 KiB
PHP
149 lines
4.4 KiB
PHP
<?php
|
|
namespace Incoviba\Service;
|
|
|
|
use DateTimeImmutable;
|
|
use DateTimeZone;
|
|
use InvalidArgumentException;
|
|
use OutOfRangeException;
|
|
use Psr\Log\LoggerInterface;
|
|
use Incoviba\Common\Ideal;
|
|
use Incoviba\Common\Implement\Exception\EmptyRedis;
|
|
use Incoviba\Common\Implement\Exception\EmptyResult;
|
|
use Incoviba\Exception\ServiceAction\{Create, Read, Update};
|
|
use Incoviba\Repository;
|
|
use Incoviba\Model;
|
|
|
|
class Job extends Ideal\Service
|
|
{
|
|
public function __construct(LoggerInterface $logger, protected Redis $redisService,
|
|
protected Repository\Job $jobRepository)
|
|
{
|
|
parent::__construct($logger);
|
|
}
|
|
protected string $redisKey = 'jobs';
|
|
|
|
public function getPending(null|string|array $orderBy = null): array
|
|
{
|
|
try {
|
|
$jobs = $this->redisService->get($this->redisKey);
|
|
if ($jobs === null) {
|
|
return [];
|
|
}
|
|
$jobs = json_decode($jobs, true);
|
|
if ($orderBy !== null) {
|
|
uksort($jobs, function($a, $b) use ($orderBy) {
|
|
return $a[$orderBy] <=> $b[$orderBy];
|
|
});
|
|
}
|
|
return array_map([$this, 'load'], $jobs);
|
|
} catch (EmptyRedis) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param int $id
|
|
* @return Model\Job
|
|
* @throws Read
|
|
*/
|
|
public function getPendingById(int $id): Model\Job
|
|
{
|
|
$jobs = $this->getJobs();
|
|
try {
|
|
$idx = $this->findJob($jobs, $id);
|
|
} catch (EmptyResult $exception) {
|
|
$exception = new OutOfRangeException('Job not found', count($jobs), $exception);
|
|
throw new Read(__CLASS__, $exception);
|
|
}
|
|
return $this->load($jobs[$idx]);
|
|
}
|
|
|
|
/**
|
|
* @param array $configuration
|
|
* @return Model\Job
|
|
* @throws Read
|
|
*/
|
|
public function add(array $configuration): Model\Job
|
|
{
|
|
$now = (new DateTimeImmutable('now', new DateTimeZone($_ENV['TZ'] ?? 'America/Santiago')));
|
|
$data = [
|
|
'id' => $now->format('Uu'),
|
|
'configuration' => $configuration,
|
|
'executed' => false,
|
|
'created_at' => $now->format('Y-m-d H:i:s'),
|
|
'updated_at' => null
|
|
];
|
|
$jobs = [];
|
|
try {
|
|
$jobs = $this->redisService->get($this->redisKey);
|
|
if ($jobs !== null) {
|
|
$jobs = json_decode($jobs, true);
|
|
}
|
|
} catch (EmptyRedis) {}
|
|
$jobs []= $data;
|
|
$this->redisService->set($this->redisKey, json_encode($jobs), -1);
|
|
return $this->load($data);
|
|
}
|
|
|
|
/**
|
|
* @param Model\Job $job
|
|
* @return bool
|
|
* @throws Read
|
|
*/
|
|
public function execute(Model\Job $job): bool
|
|
{
|
|
$jobs = $this->getJobs();
|
|
try {
|
|
$idx = $this->findJob($jobs, $job->id);
|
|
} catch (EmptyResult $exception) {
|
|
$exception = new OutOfRangeException('Job not found', count($jobs), $exception);
|
|
throw new Read(__CLASS__, $exception);
|
|
}
|
|
unset($jobs[$idx]);
|
|
$this->redisService->set($this->redisKey, json_encode(array_values($jobs)), -1);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
* @throws Read
|
|
*/
|
|
protected function getJobs(): array
|
|
{
|
|
try {
|
|
$jobs = $this->redisService->get($this->redisKey);
|
|
} catch (EmptyRedis $exception) {
|
|
throw new Read(__CLASS__, $exception);
|
|
}
|
|
if ($jobs === null) {
|
|
$exception = new InvalidArgumentException("Redis Key {$this->redisKey} not found");
|
|
throw new Read(__CLASS__, $exception);
|
|
}
|
|
return json_decode($jobs, true);
|
|
}
|
|
/**
|
|
* @param array $jobs
|
|
* @param int $id
|
|
* @return int
|
|
* @throws EmptyResult
|
|
*/
|
|
protected function findJob(array $jobs, int $id): int
|
|
{
|
|
$idx = array_find_key($jobs, function($job) use ($id) {
|
|
return (int) $job['id'] === $id;
|
|
});
|
|
if ($idx === null) {
|
|
throw new EmptyResult("SELECT * FROM jobs WHERE id = ?");
|
|
}
|
|
return $idx;
|
|
}
|
|
protected function load(array $data, ?int $id = null): Model\Job
|
|
{
|
|
$job = new Model\Job();
|
|
$job->id = $id ?? $data['id'] ?? null;
|
|
$job->configuration = $data['configuration'] ?? [];
|
|
$job->executed = $data['executed'] ?? false;
|
|
return $job;
|
|
}
|
|
}
|