Merge pull request #23419 from chrisgaraffa/contactheader-regions
[civicrm-core.git] / Civi / Api4 / Action / Queue / RunItems.php
CommitLineData
8369e17b
TO
1<?php
2
3namespace Civi\Api4\Action\Queue;
4
677170bd
TO
5use Civi\Core\Event\GenericHookEvent;
6
8369e17b
TO
7/**
8 * Run an enqueued item (task).
9 *
10 * You must either:
11 *
12 * - (a) Give the target queue-item specifically (`setItem()`). Useful if you called `claimItem()` separately.
13 * - (b) Give the name of the queue from which to find an item (`setQueue()`).
14 *
15 * Note: If you use `setItem()`, the inputted will be validated (refetched) to ensure authenticity of all details.
16 *
17 * Returns 0 or 1 records which indicate the outcome of running the chosen task.
18 *
19 * ```php
20 * $todo = Civi\Api4\Queue::claimItem()->setQueue($item)->setLeaseTime(600)->execute()->single();
21 * $result = Civi\Api4\Queue::runItem()->setItem($todo)->execute()->single();
22 * assert(in_array($result['outcome'], ['ok', 'retry', 'fail']))
23 *
24 * $result = Civi\Api4\Queue::runItem()->setQueue('foo')->execute()->first();
25 * assert(in_array($result['outcome'], ['ok', 'retry', 'fail']))
26 * ```
27 *
28 * Valid outcomes are:
29 * - 'ok': Task executed normally. Removed from queue.
30 * - 'retry': Task encountered an error. Will try again later.
31 * - 'fail': Task encountered an error. Will not try again later. Removed from queue.
32 *
33 * @method $this setItem(?array $item)
34 * @method ?array getItem()
35 * @method ?string setQueue
36 * @method $this setQueue(?string $queue)
37 */
38class RunItems extends \Civi\Api4\Generic\AbstractAction {
39
40 /**
41 * Previously claimed item - which should now be released.
42 *
43 * @var array|null
44 * Fields: {id: scalar, queue: string}
45 */
46 protected $items;
47
48 /**
49 * Name of the target queue.
50 *
51 * @var string|null
52 */
53 protected $queue;
54
55 public function _run(\Civi\Api4\Generic\Result $result) {
56 if (!empty($this->items)) {
57 $this->validateItemStubs();
58 $queue = \Civi::queue($this->items[0]['queue']);
59 $ids = \CRM_Utils_Array::collect('id', $this->items);
60 if (count($ids) > 1 && !($queue instanceof \CRM_Queue_Queue_BatchQueueInterface)) {
61 throw new \API_Exception("runItems: Error: Running multiple items requires BatchQueueInterface");
62 }
63 if (count($ids) > 1) {
64 $items = $queue->fetchItems($ids);
65 }
66 else {
67 $items = [$queue->fetchItem($ids[0])];
68 }
69 }
70 elseif (!empty($this->queue)) {
71 $queue = \Civi::queue($this->queue);
72 if (!$queue->isActive()) {
73 return;
74 }
75 $items = $queue instanceof \CRM_Queue_Queue_BatchQueueInterface
76 ? $queue->claimItems($queue->getSpec('batch_limit') ?: 1)
77 : [$queue->claimItem()];
78 }
79 else {
80 throw new \API_Exception("runItems: Requires either 'queue' or 'item'.");
81 }
82
83 if (empty($items)) {
84 return;
85 }
86
87 $outcomes = [];
88 \CRM_Utils_Hook::queueRun($queue, $items, $outcomes);
89 if (empty($outcomes)) {
90 throw new \API_Exception(sprintf('Failed to run queue items (name=%s, runner=%s, itemCount=%d, outcomeCount=%d)',
91 $queue->getName(), $queue->getSpec('runner'), count($items), count($outcomes)));
92 }
93 foreach ($items as $itemPos => $item) {
94 $result[] = ['outcome' => $outcomes[$itemPos], 'item' => $this->createItemStub($item)];
95 }
677170bd
TO
96
97 \Civi::dispatcher()->dispatch('civi.queue.check', GenericHookEvent::create([
98 'queue' => $queue,
99 ]));
8369e17b
TO
100 }
101
102 private function validateItemStubs(): void {
103 $queueNames = [];
104 if (!isset($this->items[0])) {
105 throw new \API_Exception("Queue items must be given as numeric array.");
106 }
107 foreach ($this->items as $item) {
108 if (empty($item['queue'])) {
109 throw new \API_Exception("Queue item requires property 'queue'.");
110 }
111 if (empty($item['id'])) {
112 throw new \API_Exception("Queue item requires property 'id'.");
113 }
114 $queueNames[$item['queue']] = 1;
115 }
116 if (count($queueNames) > 1) {
117 throw new \API_Exception("Queue items cannot be mixed. Found queues: " . implode(', ', array_keys($queueNames)));
118 }
119 }
120
121 private function createItemStub($item): array {
122 return ['id' => $item->id, 'queue' => $item->queue_name];
123 }
124
125}