Skip to content

Commit d5a048f

Browse files
FOUR-32194: New artisan command: transition-executors
FOUR-32194: New artisan command: transition-executors FOUR-32194: New artisan command: transition-executors FOUR-32194: New artisan command: transition-executors FOUR-32194: New artisan command: transition-executors FOUR-32194: New artisan command: transition-executors
1 parent 3088c8f commit d5a048f

3 files changed

Lines changed: 464 additions & 1 deletion

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
namespace ProcessMaker\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Database\Eloquent\Collection;
7+
use Illuminate\Http\Client\RequestException;
8+
use ProcessMaker\Enums\ScriptExecutorType;
9+
use ProcessMaker\Models\ScriptExecutor;
10+
use ProcessMaker\Services\ScriptMicroserviceService;
11+
use Illuminate\Support\Facades\Log;
12+
13+
class TransitionExecutors extends Command
14+
{
15+
/**
16+
* The name and signature of the console command.
17+
*
18+
* @var string
19+
*/
20+
protected $signature = 'processmaker:transition-executors
21+
{uuid : The script executor UUID, or "all"}';
22+
23+
/**
24+
* The console command description.
25+
*
26+
* @var string
27+
*/
28+
protected $description = 'Transition non-default script executor(s) to the Script Microservice';
29+
30+
public function __construct(private ScriptMicroserviceService $scriptMicroserviceService)
31+
{
32+
parent::__construct();
33+
}
34+
35+
/**
36+
* Execute the console command.
37+
*
38+
* Always talks to the microservice, even when SCRIPT_MICROSERVICE_ENABLED is false.
39+
*/
40+
public function handle(): int
41+
{
42+
$executors = $this->resolveExecutors($this->argument('uuid'));
43+
44+
if ($executors === null) {
45+
return 1;
46+
}
47+
48+
if ($executors->isEmpty()) {
49+
$this->warn('No script executors found to transition.');
50+
51+
return 0;
52+
}
53+
54+
$isAll = $this->argument('uuid') === 'all';
55+
$remaining = $executors->count();
56+
$processed = 0;
57+
58+
foreach ($executors as $executor) {
59+
$remaining--;
60+
$this->info("Transitioning executor {$executor->uuid} ({$executor->language}) to the microservice...");
61+
62+
try {
63+
$response = $this->scriptMicroserviceService->updateCustomExecutor($executor);
64+
Log::debug('Response', ['response' => $response]);
65+
$status = strtolower((string) ($response['status'] ?? ''));
66+
67+
if (in_array($status, ['error', 'failed', 'failure'], true) && !isset($response['executor_id'])) {
68+
throw new \RuntimeException(json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) ?: 'Transition failed');
69+
}
70+
} catch (RequestException $e) {
71+
$this->error("Transition failed for executor {$executor->uuid}");
72+
$this->line($e->response?->body() ?: $e->getMessage());
73+
if ($isAll && $remaining > 0) {
74+
$this->warn("Stopping: {$remaining} remaining executor(s) were not processed.");
75+
}
76+
77+
return 1;
78+
} catch (\Throwable $e) {
79+
$this->error("Transition failed for executor {$executor->uuid}");
80+
$this->line($e->getMessage());
81+
if ($isAll && $remaining > 0) {
82+
$this->warn("Stopping: {$remaining} remaining executor(s) were not processed.");
83+
}
84+
85+
return 1;
86+
}
87+
88+
$this->info("Executor {$executor->uuid} transitioned successfully.");
89+
$processed++;
90+
}
91+
92+
if ($isAll) {
93+
$this->info("All script executors transitioned successfully. ({$processed} processed)");
94+
}
95+
96+
return 0;
97+
}
98+
99+
/**
100+
* Resolve executors that should be transitioned.
101+
*
102+
* Includes:
103+
* - type = custom
104+
* - type null (or unset) that are NOT the default/first executor for their language
105+
*
106+
* Excludes:
107+
* - default package executors (first row per language)
108+
*
109+
* @return Collection<int, ScriptExecutor>|null Null when the request is invalid.
110+
*/
111+
private function resolveExecutors(string $uuid): ?Collection
112+
{
113+
if ($uuid === 'all') {
114+
return ScriptExecutor::query()
115+
->orderBy('id')
116+
->get()
117+
->filter(fn (ScriptExecutor $executor) => $this->shouldTransition($executor))
118+
->values();
119+
}
120+
121+
if (!$this->isValidUuid($uuid)) {
122+
$this->error('Invalid uuid. Provide a script executor UUID or "all".');
123+
124+
return null;
125+
}
126+
127+
$executor = ScriptExecutor::where('uuid', $uuid)->first();
128+
129+
if (!$executor) {
130+
$this->error("Script executor [{$uuid}] not found.");
131+
132+
return null;
133+
}
134+
135+
if (!$this->shouldTransition($executor)) {
136+
$this->error("Script executor [{$uuid}] is a default/system executor and cannot be transitioned.");
137+
138+
return null;
139+
}
140+
141+
return new Collection([$executor]);
142+
}
143+
144+
/**
145+
* Whether this executor should be migrated to the microservice.
146+
*
147+
* Custom executors always qualify. Others qualify only when they are not
148+
* the default (first installed) executor for their language.
149+
*/
150+
private function shouldTransition(ScriptExecutor $executor): bool
151+
{
152+
if ($executor->type === ScriptExecutorType::Custom) {
153+
return true;
154+
}
155+
156+
$initial = ScriptExecutor::query()
157+
->where('language', $executor->language)
158+
->orderBy('created_at')
159+
->orderBy('id')
160+
->first();
161+
162+
return !$initial || (int) $initial->id !== (int) $executor->id;
163+
}
164+
165+
private function isValidUuid(string $uuid): bool
166+
{
167+
return (bool) preg_match(
168+
'/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
169+
$uuid
170+
);
171+
}
172+
}

resources/views/admin/script-executors/index.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<div id="script-executors" class="px-3">
1919
<div class="card card-body">
2020
<script-executors
21-
:script_microservice_enabled="{{$script_microservice_enabled}}"
21+
:script_microservice_enabled="@json($script_microservice_enabled)"
2222
></script-executors>
2323
</div>
2424
</div>

0 commit comments

Comments
 (0)