diff --git a/application/Espo/Core/Job/Data.php b/application/Espo/Core/Job/Data.php new file mode 100644 index 0000000000..42c7c62db2 --- /dev/null +++ b/application/Espo/Core/Job/Data.php @@ -0,0 +1,99 @@ +data = $data ?? (object) []; + } + + public static function create(?stdClass $data = null): self + { + return new self($data); + } + + public function getRaw(): stdClass + { + return ObjectUtil::clone($this->data); + } + + /** + * @return mixed + */ + public function get(string $name) + { + return $this->getRaw()->$name ?? null; + } + + public function has(string $name): bool + { + return property_exists($this->data, $name); + } + + public function getTargetId(): ?string + { + return $this->targetId; + } + + public function getTargetType(): ?string + { + return $this->targetType; + } + + public function withTargetId(?string $targetId): self + { + $obj = clone $this; + + $obj->targetId = $targetId; + + return $obj; + } + + public function withTargetType(?string $targetType): self + { + $obj = clone $this; + + $obj->targetType = $targetType; + + return $obj; + } +} diff --git a/application/Espo/Core/Job/Job.php b/application/Espo/Core/Job/Job.php index 86eca9ac9e..72906399cd 100644 --- a/application/Espo/Core/Job/Job.php +++ b/application/Espo/Core/Job/Job.php @@ -30,12 +30,12 @@ namespace Espo\Core\Job; /** - * A job. Processed by the Cron or Daemon. Running is conducted according a scheduling of a scheduled job record. + * A job. */ interface Job { /** * Run a job. */ - public function run(): void; + public function run(Data $data): void; } diff --git a/application/Espo/Core/Job/JobDataLess.php b/application/Espo/Core/Job/JobDataLess.php new file mode 100644 index 0000000000..9e02091831 --- /dev/null +++ b/application/Espo/Core/Job/JobDataLess.php @@ -0,0 +1,41 @@ +runInternal($job, false); + $this->runInternal($jobEntity, false); } /** @@ -80,9 +81,9 @@ class JobRunner * * @throws Throwable */ - public function runThrowingException(JobEntity $job): void + public function runThrowingException(JobEntity $jobEntity): void { - $this->runInternal($job, true); + $this->runInternal($jobEntity, true); } /** @@ -95,29 +96,29 @@ class JobRunner throw new Error(); } - $job = $this->entityManager->getEntity('Job', $id); + $jobEntity = $this->entityManager->getEntity('Job', $id); - if (!$job) { - throw new Error("Job {$id} not found."); + if (!$jobEntity) { + throw new Error("Job '{$id}' not found."); } - if ($job->getStatus() !== JobStatus::READY) { - throw new Error("Can't run job {$id} with no status Ready."); + if ($jobEntity->getStatus() !== JobStatus::READY) { + throw new Error("Can't run job '{$id}' with no status Ready."); } - if (!$job->getStartedAt()) { - $job->set('startedAt', date('Y-m-d H:i:s')); + if (!$jobEntity->getStartedAt()) { + $jobEntity->set('startedAt', date('Y-m-d H:i:s')); } - $job->set('status', JobStatus::RUNNING); - $job->set('pid', System::getPid()); + $jobEntity->set('status', JobStatus::RUNNING); + $jobEntity->set('pid', System::getPid()); - $this->entityManager->saveEntity($job); + $this->entityManager->saveEntity($jobEntity); - $this->run($job); + $this->run($jobEntity); } - private function runInternal(JobEntity $job, bool $throwException = false): void + private function runInternal(JobEntity $jobEntity, bool $throwException = false): void { $isSuccess = true; @@ -126,17 +127,17 @@ class JobRunner $exception = null; try { - if ($job->getScheduledJobId()) { - $this->runScheduledJob($job); + if ($jobEntity->getScheduledJobId()) { + $this->runScheduledJob($jobEntity); } - else if ($job->getJob()) { - $this->runJobByName($job); + else if ($jobEntity->getJob()) { + $this->runJobNamed($jobEntity); } - else if ($job->getServiceName()) { - $this->runService($job); + else if ($jobEntity->getServiceName()) { + $this->runService($jobEntity); } else { - $id = $job->getId(); + $id = $jobEntity->getId(); throw new Error("Not runnable job '{$id}'."); } @@ -145,7 +146,7 @@ class JobRunner $isSuccess = false; $this->log->error( - "JobManager: Failed job running, job '{$job->id}'. " . + "JobManager: Failed job running, job '{$jobEntity->id}'. " . $e->getMessage() . "; at " . $e->getFile() . ":" . $e->getLine() . "." ); @@ -156,83 +157,81 @@ class JobRunner $status = $isSuccess ? JobStatus::SUCCESS : JobStatus::FAILED; - $job->set('status', $status); + $jobEntity->set('status', $status); if ($isSuccess) { - $job->set('executedAt', date('Y-m-d H:i:s')); + $jobEntity->set('executedAt', date('Y-m-d H:i:s')); } - $this->entityManager->saveEntity($job); + $this->entityManager->saveEntity($jobEntity); if ($throwException && $exception) { throw new $exception($exception->getMessage()); } - if ($job->getScheduledJobId() && !$skipLog) { + if ($jobEntity->getScheduledJobId() && !$skipLog) { $this->scheduleUtil->addLogRecord( - $job->getScheduledJobId(), + $jobEntity->getScheduledJobId(), $status, null, - $job->getTargetId(), - $job->getTargetType() + $jobEntity->getTargetId(), + $jobEntity->getTargetType() ); } } - protected function runJobByName(JobEntity $job): void + private function runJobNamed(JobEntity $jobEntity): void { - $jobName = $job->getJob(); - - $obj = $this->jobFactory->create($jobName); - - if ($obj instanceof JobTargeted) { - if ( - $job->getTargetType() === null || - $job->getTargetId() === null - ) { - throw new Error("Can't run targeted job '{$jobName}' w/o target."); - } - - $obj->run($job->getTargetType(), $job->getTargetId(), $job->getData()); - - return; - } - - if (!method_exists($obj, 'run')) { - throw new Error("No 'run' method in job '{$jobName}'."); - } - - $obj->run(); - } - - protected function runScheduledJob(JobEntity $job): void - { - $jobName = $job->getScheduledJobJob(); + $jobName = $jobEntity->getJob(); if (!$jobName) { throw new Error( - "Can't run job '" . $job->getId() . "'. Not a scheduled job." + "Can't run job '" . $jobEntity->getId() . "'. No job name." ); } - $obj = $this->jobFactory->create($jobName); + $job = $this->jobFactory->create($jobName); - if ($obj instanceof JobTargeted) { - $obj->run($job->getTargetType(), $job->getTargetId(), $job->getData()); - - return; - } - - if (!method_exists($obj, 'run')) { - throw new Error("No 'run' method in job '{$jobName}'."); - } - - $obj->run(); + $this->runJob($job, $jobEntity); } - protected function runService(JobEntity $job): void + private function runScheduledJob(JobEntity $jobEntity): void { - $serviceName = $job->getServiceName(); + $jobName = $jobEntity->getScheduledJobJob(); + + if (!$jobName) { + throw new Error( + "Can't run job '" . $jobEntity->getId() . "'. Not a scheduled job." + ); + } + + $job = $this->jobFactory->create($jobName); + + $this->runJob($job, $jobEntity); + } + + /** + * @param Job|JobDataLess $job + */ + private function runJob($job, JobEntity $jobEntity): void + { + if ( + !$job instanceof Job && + !$job instanceof JobDataLess + ) { + throw new TypeError(); + } + + $data = Data::create($jobEntity->getData()) + ->withTargetId($jobEntity->getTargetId()) + ->withTargetType($jobEntity->getTargetType()); + + $job->run($data); + } + + private function runService(JobEntity $jobEntity): void + { + $serviceName = $jobEntity->getServiceName(); if (!$serviceName) { throw new Error("Job with empty serviceName."); @@ -244,7 +243,7 @@ class JobRunner $service = $this->serviceFactory->create($serviceName); - $methodName = $job->getMethodName(); + $methodName = $jobEntity->getMethodName(); if (!$methodName) { throw new Error('Job with empty methodName.'); @@ -254,6 +253,6 @@ class JobRunner throw new Error("No method '{$methodName}' in service '{$serviceName}'."); } - $service->$methodName($job->getData(), $job->getTargetId(), $job->getTargetType()); + $service->$methodName($jobEntity->getData(), $jobEntity->getTargetId(), $jobEntity->getTargetType()); } } diff --git a/application/Espo/Core/Jobs/Base.php b/application/Espo/Core/Jobs/Base.php index ad8d993b56..3a21bd02da 100644 --- a/application/Espo/Core/Jobs/Base.php +++ b/application/Espo/Core/Jobs/Base.php @@ -31,6 +31,9 @@ namespace Espo\Core\Jobs; use Espo\Core\Container; +/** + * @deprecated + */ abstract class Base { private $container; @@ -69,6 +72,5 @@ abstract class Base { $this->container = $container; } - } diff --git a/application/Espo/Core/Jobs/Job.php b/application/Espo/Core/Jobs/Job.php index c4469ccdd9..6e7a50749a 100644 --- a/application/Espo/Core/Jobs/Job.php +++ b/application/Espo/Core/Jobs/Job.php @@ -32,6 +32,6 @@ namespace Espo\Core\Jobs; /** * @deprecated Use `Espo\Core\Job\Job` instead. */ -interface Job extends \Espo\Core\Job\Job +interface Job extends \Espo\Core\Job\JobDataLess { } diff --git a/application/Espo/Jobs/AuthTokenControl.php b/application/Espo/Jobs/AuthTokenControl.php index 50000f560f..b6d2cd1458 100644 --- a/application/Espo/Jobs/AuthTokenControl.php +++ b/application/Espo/Jobs/AuthTokenControl.php @@ -32,16 +32,16 @@ namespace Espo\Jobs; use Espo\Core\{ Utils\Config, ORM\EntityManager, - Job\Job, + Job\JobDataLess, }; use DateTime; -class AuthTokenControl implements Job +class AuthTokenControl implements JobDataLess { - protected $config; + private $config; - protected $entityManager; + private $entityManager; public function __construct(Config $config, EntityManager $entityManager) { diff --git a/application/Espo/Jobs/CheckEmailAccounts.php b/application/Espo/Jobs/CheckEmailAccounts.php index d252bdc052..e02359786a 100644 --- a/application/Espo/Jobs/CheckEmailAccounts.php +++ b/application/Espo/Jobs/CheckEmailAccounts.php @@ -33,7 +33,8 @@ use Espo\Core\Exceptions\Error; use Espo\Core\{ Job\JobStatus, - Job\JobTargeted, + Job\JobPreperable, + Job\Data, ServiceFactory, ORM\EntityManager, }; @@ -41,13 +42,12 @@ use Espo\Core\{ use Espo\Entities\ScheduledJob; use Throwable; -use StdClass; -class CheckEmailAccounts implements JobTargeted +class CheckEmailAccounts implements JobPreperable { - protected $serviceFactory; + private $serviceFactory; - protected $entityManager; + private $entityManager; public function __construct(ServiceFactory $serviceFactory, EntityManager $entityManager) { @@ -55,10 +55,12 @@ class CheckEmailAccounts implements JobTargeted $this->entityManager = $entityManager; } - public function run(string $targetType, string $targetId, StdClass $data): void + public function run(Data $data): void { + $targetId = $data->getTargetId(); + if (!$targetId) { - throw new Error(); + throw new Error("No target."); } $service = $this->serviceFactory->create('EmailAccount'); diff --git a/application/Espo/Jobs/CheckInboundEmails.php b/application/Espo/Jobs/CheckInboundEmails.php index 280af47fe0..d8f787730d 100644 --- a/application/Espo/Jobs/CheckInboundEmails.php +++ b/application/Espo/Jobs/CheckInboundEmails.php @@ -33,7 +33,8 @@ use Espo\Core\Exceptions\Error; use Espo\Core\{ Job\JobStatus, - Job\JobTargeted, + Job\JobPreperable, + Job\Data, ServiceFactory, ORM\EntityManager, }; @@ -41,13 +42,12 @@ use Espo\Core\{ use Espo\Entities\ScheduledJob; use Throwable; -use StdClass; -class CheckInboundEmails implements JobTargeted +class CheckInboundEmails implements JobPreperable { - protected $serviceFactory; + private $serviceFactory; - protected $entityManager; + private $entityManager; public function __construct(ServiceFactory $serviceFactory, EntityManager $entityManager) { @@ -55,13 +55,16 @@ class CheckInboundEmails implements JobTargeted $this->entityManager = $entityManager; } - public function run(string $targetType, string $targetId, StdClass $data): void + public function run(Data $data): void { + $targetId = $data->getTargetId(); + if (!$targetId) { - throw new Error(); + throw new Error("No target."); } $service = $this->serviceFactory->create('InboundEmail'); + $entity = $this->entityManager->getEntity('InboundEmail', $targetId); if (!$entity) { diff --git a/application/Espo/Jobs/CheckNewVersion.php b/application/Espo/Jobs/CheckNewVersion.php index ffde647670..cc699a6f78 100644 --- a/application/Espo/Jobs/CheckNewVersion.php +++ b/application/Espo/Jobs/CheckNewVersion.php @@ -32,13 +32,13 @@ namespace Espo\Jobs; use Espo\Core\{ Utils\Config, ORM\EntityManager, - Job\Job, + Job\JobDataLess, }; use DateTime; use DateTimeZone; -class CheckNewVersion implements Job +class CheckNewVersion implements JobDataLess { protected $config; diff --git a/application/Espo/Jobs/Cleanup.php b/application/Espo/Jobs/Cleanup.php index 9c5393a733..bcf9418ef2 100644 --- a/application/Espo/Jobs/Cleanup.php +++ b/application/Espo/Jobs/Cleanup.php @@ -32,7 +32,7 @@ namespace Espo\Jobs; use Espo\Core\{ Utils\Config, ORM\EntityManager, - Job\Job, + Job\JobDataLess, Utils\Metadata, Utils\File\Manager as FileManager, InjectableFactory, @@ -48,7 +48,7 @@ use SplFileInfo; use Exception; use Throwable; -class Cleanup implements Job +class Cleanup implements JobDataLess { protected $cleanupJobPeriod = '10 days'; diff --git a/application/Espo/Jobs/Dummy.php b/application/Espo/Jobs/Dummy.php index 860da849dd..1c6b55ac97 100644 --- a/application/Espo/Jobs/Dummy.php +++ b/application/Espo/Jobs/Dummy.php @@ -30,10 +30,10 @@ namespace Espo\Jobs; use Espo\Core\{ - Job\Job, + Job\JobDataLess, }; -class Dummy implements Job +class Dummy implements JobDataLess { public function run(): void { diff --git a/application/Espo/Jobs/ProcessJobQueueE0.php b/application/Espo/Jobs/ProcessJobQueueE0.php index 82a185d1c5..2299d7c1cd 100644 --- a/application/Espo/Jobs/ProcessJobQueueE0.php +++ b/application/Espo/Jobs/ProcessJobQueueE0.php @@ -32,11 +32,11 @@ namespace Espo\Jobs; use Espo\Core\{ Job\JobManager, Utils\Config, - Job\Job, + Job\JobDataLess, Job\QueueName, }; -class ProcessJobQueueE0 implements Job +class ProcessJobQueueE0 implements JobDataLess { private $jobManager; diff --git a/application/Espo/Jobs/ProcessJobQueueQ0.php b/application/Espo/Jobs/ProcessJobQueueQ0.php index 3eb18da4bc..531fbbbdf6 100644 --- a/application/Espo/Jobs/ProcessJobQueueQ0.php +++ b/application/Espo/Jobs/ProcessJobQueueQ0.php @@ -32,11 +32,11 @@ namespace Espo\Jobs; use Espo\Core\{ Job\JobManager, Utils\Config, - Job\Job, + Job\JobDataLess, Job\QueueName, }; -class ProcessJobQueueQ0 implements Job +class ProcessJobQueueQ0 implements JobDataLess { private $jobManager; diff --git a/application/Espo/Jobs/ProcessJobQueueQ1.php b/application/Espo/Jobs/ProcessJobQueueQ1.php index 678df8f338..6571068fa7 100644 --- a/application/Espo/Jobs/ProcessJobQueueQ1.php +++ b/application/Espo/Jobs/ProcessJobQueueQ1.php @@ -32,11 +32,11 @@ namespace Espo\Jobs; use Espo\Core\{ Job\JobManager, Utils\Config, - Job\Job, + Job\JobDataLess, Job\QueueName, }; -class ProcessJobQueueQ1 implements Job +class ProcessJobQueueQ1 implements JobDataLess { private $jobManager; diff --git a/application/Espo/Jobs/ProcessWebhookQueue.php b/application/Espo/Jobs/ProcessWebhookQueue.php index 008cbc6e39..10c272c7e6 100644 --- a/application/Espo/Jobs/ProcessWebhookQueue.php +++ b/application/Espo/Jobs/ProcessWebhookQueue.php @@ -30,11 +30,11 @@ namespace Espo\Jobs; use Espo\Core\{ - Job\Job, + Job\JobDataLess, Webhook\Queue, }; -class ProcessWebhookQueue implements Job +class ProcessWebhookQueue implements JobDataLess { private $queue; diff --git a/application/Espo/Jobs/SendEmailNotifications.php b/application/Espo/Jobs/SendEmailNotifications.php index bc83045081..215a4379cb 100644 --- a/application/Espo/Jobs/SendEmailNotifications.php +++ b/application/Espo/Jobs/SendEmailNotifications.php @@ -29,11 +29,11 @@ namespace Espo\Jobs; -use Espo\Core\Job\Job; +use Espo\Core\Job\JobDataLess; use Espo\Tools\EmailNotification\Processor; -class SendEmailNotifications implements Job +class SendEmailNotifications implements JobDataLess { private $processor; diff --git a/application/Espo/Modules/Crm/Jobs/ControlKnowledgeBaseArticleStatus.php b/application/Espo/Modules/Crm/Jobs/ControlKnowledgeBaseArticleStatus.php index 121d2bd277..6b075313dd 100644 --- a/application/Espo/Modules/Crm/Jobs/ControlKnowledgeBaseArticleStatus.php +++ b/application/Espo/Modules/Crm/Jobs/ControlKnowledgeBaseArticleStatus.php @@ -24,10 +24,10 @@ namespace Espo\Modules\Crm\Jobs; use Espo\Core\{ ORM\EntityManager, - Job\Job, + Job\JobDataLess, }; -class ControlKnowledgeBaseArticleStatus implements Job +class ControlKnowledgeBaseArticleStatus implements JobDataLess { protected $entityManager; diff --git a/application/Espo/Modules/Crm/Jobs/ProcessMassEmail.php b/application/Espo/Modules/Crm/Jobs/ProcessMassEmail.php index a1784da585..689f87679a 100644 --- a/application/Espo/Modules/Crm/Jobs/ProcessMassEmail.php +++ b/application/Espo/Modules/Crm/Jobs/ProcessMassEmail.php @@ -31,7 +31,7 @@ namespace Espo\Modules\Crm\Jobs; use Espo\Core\{ ORM\EntityManager, - Job\Job, + Job\JobDataLess, Utils\Log, }; @@ -42,7 +42,7 @@ use Espo\{ use Throwable; -class ProcessMassEmail implements Job +class ProcessMassEmail implements JobDataLess { private $processor; diff --git a/application/Espo/Modules/Crm/Jobs/SendEmailReminders.php b/application/Espo/Modules/Crm/Jobs/SendEmailReminders.php index 94a01ea095..d45b67942e 100644 --- a/application/Espo/Modules/Crm/Jobs/SendEmailReminders.php +++ b/application/Espo/Modules/Crm/Jobs/SendEmailReminders.php @@ -33,7 +33,7 @@ use Espo\Core\{ InjectableFactory, ORM\EntityManager, Utils\Config, - Job\Job, + Job\JobDataLess, Utils\Log, }; @@ -43,7 +43,7 @@ use Throwable; use DateTime; use DateInterval; -class SendEmailReminders implements Job +class SendEmailReminders implements JobDataLess { private const MAX_PORTION_SIZE = 10; diff --git a/application/Espo/Modules/Crm/Jobs/SubmitPopupReminders.php b/application/Espo/Modules/Crm/Jobs/SubmitPopupReminders.php index d94fcc196d..8f7b5d9266 100644 --- a/application/Espo/Modules/Crm/Jobs/SubmitPopupReminders.php +++ b/application/Espo/Modules/Crm/Jobs/SubmitPopupReminders.php @@ -33,14 +33,14 @@ use Espo\Core\{ ORM\EntityManager, Utils\Config, WebSocket\Submission as WebSocketSubmission, - Job\Job, + Job\JobDataLess, Utils\Log, }; use Throwable; use DateTime; -class SubmitPopupReminders implements Job +class SubmitPopupReminders implements JobDataLess { private const REMINDER_PAST_HOURS = 24;