diff --git a/ProcessMaker/Console/Commands/BuildScriptExecutors.php b/ProcessMaker/Console/Commands/BuildScriptExecutors.php index 963d2b2803..0bc35ee5e5 100644 --- a/ProcessMaker/Console/Commands/BuildScriptExecutors.php +++ b/ProcessMaker/Console/Commands/BuildScriptExecutors.php @@ -9,6 +9,7 @@ use ProcessMaker\Exception\InvalidDockerImageException; use ProcessMaker\Facades\Docker; use ProcessMaker\Models\ScriptExecutor; +use ProcessMaker\ScriptRunners\Base; use UnexpectedValueException; class BuildScriptExecutors extends Command @@ -161,7 +162,7 @@ public function buildExecutor() $this->execCommand($command); - $isNayra = $scriptExecutor->language === 'php-nayra'; + $isNayra = $scriptExecutor->language === Base::NAYRA_LANG; if ($isNayra) { $instanceName = config('app.instance'); $this->info('Stop existing nayra container'); diff --git a/ProcessMaker/Console/Commands/DockerExecutorPhpNayra.php b/ProcessMaker/Console/Commands/DockerExecutorPhpNayra.php new file mode 100644 index 0000000000..08586bc99f --- /dev/null +++ b/ProcessMaker/Console/Commands/DockerExecutorPhpNayra.php @@ -0,0 +1,46 @@ +exists(); + if (!$exists) { + ScriptExecutor::install([ + 'language' => Base::NAYRA_LANG, + 'title' => 'Nayra (µService)', + 'description' => 'Nayra (µService) Executor', + 'config' => '', + ]); + } + + // Build the instance image. This is the same as if you were to build it from the admin UI + Artisan::call('processmaker:build-script-executor ' . Base::NAYRA_LANG . ' --rebuild'); + } +} diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/TaskController.php b/ProcessMaker/Http/Controllers/Api/V1_1/TaskController.php index 047b24f1da..4a7dd3bfc5 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/TaskController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/TaskController.php @@ -4,15 +4,22 @@ namespace ProcessMaker\Http\Controllers\Api\V1_1; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Http\Request; use ProcessMaker\Http\Controllers\Controller; -use ProcessMaker\Models\ProcessRequestToken; +use ProcessMaker\Http\Resources\V1_1\TaskInterstitialResource; +use ProcessMaker\Http\Resources\V1_1\TaskResource; use ProcessMaker\Http\Resources\V1_1\TaskScreen; +use ProcessMaker\Models\ProcessRequestToken; class TaskController extends Controller { protected $defaultFields = [ 'id', + 'element_id', 'element_name', + 'element_type', + 'status', 'due_at', ]; @@ -24,18 +31,50 @@ public function index() $query = ProcessRequestToken::select($this->defaultFields) ->where('element_type', 'task'); - return $query->paginate(); + $this->processFilters(request(), $query); + $pagination = $query->paginate(); + $perPage = $pagination->perPage(); + $page = $pagination->currentPage(); + $lastPage = $pagination->lastPage(); + return [ + 'data' => $pagination->items(), + 'meta' => [ + 'total' => $pagination->total(), + 'perPage' => $pagination->perPage(), + 'currentPage' => $pagination->currentPage(), + 'lastPage' => $pagination->lastPage(), + 'count' => $pagination->count(), + 'from' => $perPage * ($page - 1) + 1, + 'last_page' => $lastPage, + 'path' => '/', + 'per_page' => $perPage, + 'to' => $perPage * ($page - 1) + $perPage, + 'total_pages' => ceil($pagination->count() / $perPage), + ], + ]; + } + + private function processFilters(Request $request, Builder $query) + { + if ($request->has('process_request_id')) { + $query->where('process_request_id', $request->input('process_request_id')); + } + if ($request->has('status')) { + $query->where('status', $request->input('status')); + } } public function show(ProcessRequestToken $task) { - return $task; + $resource = TaskResource::preprocessInclude(request(), ProcessRequestToken::where('id', $task->id)); + return $resource->toArray(request()); } public function showScreen($taskId) { - $task = ProcessRequestToken::select('id', 'process_request_id', 'element_id', 'process_id') - ->findOrFail($taskId); + $task = ProcessRequestToken::select( + array_merge($this->defaultFields, ['process_request_id', 'process_id']) + )->findOrFail($taskId); $response = new TaskScreen($task); $response = response($response->toArray(request())['screen'], 200); $now = time(); @@ -45,4 +84,19 @@ public function showScreen($taskId) $response->headers->set('Expires', gmdate('D, d M Y H:i:s', $now + $cacheTime) . ' GMT'); return $response; } + + public function showInterstitial($taskId) + { + $task = ProcessRequestToken::select( + array_merge($this->defaultFields, ['process_request_id', 'process_id']) + )->findOrFail($taskId); + $response = new TaskInterstitialResource($task); + $response = response($response->toArray(request())['screen'], 200); + $now = time(); + // screen cache time + $cacheTime = config('screen_task_cache_time', 86400); + $response->headers->set('Cache-Control', 'max-age=' . $cacheTime . ', must-revalidate, public'); + $response->headers->set('Expires', gmdate('D, d M Y H:i:s', $now + $cacheTime) . ' GMT'); + return $response; + } } diff --git a/ProcessMaker/Http/Resources/V1_1/TaskInterstitialResource.php b/ProcessMaker/Http/Resources/V1_1/TaskInterstitialResource.php new file mode 100644 index 0000000000..cedfa8a5f4 --- /dev/null +++ b/ProcessMaker/Http/Resources/V1_1/TaskInterstitialResource.php @@ -0,0 +1,19 @@ +includeInterstitial(); + } +} diff --git a/ProcessMaker/Http/Resources/V1_1/TaskResource.php b/ProcessMaker/Http/Resources/V1_1/TaskResource.php index 7877910217..dd9b948f23 100644 --- a/ProcessMaker/Http/Resources/V1_1/TaskResource.php +++ b/ProcessMaker/Http/Resources/V1_1/TaskResource.php @@ -42,6 +42,8 @@ class TaskResource extends ApiResource 'id', 'element_name', 'element_id', + 'element_type', + 'status', 'due_at', ]; @@ -53,7 +55,7 @@ class TaskResource extends ApiResource protected static $defaultFieldsFor = [ 'user' => ['id', 'firstname', 'lastname', 'email', 'username', 'avatar'], 'requestor' => ['id', 'first_name', 'last_name', 'email'], - 'processRequest' => ['id', 'process_id', 'status'], + 'processRequest' => ['id', 'process_id', 'process_version_id', 'callable_id', 'status'], 'draft' => ['id', 'task_id', 'data'], 'screen' => ['id', 'config'], 'process' => ['id', 'name'], @@ -68,12 +70,11 @@ class TaskResource extends ApiResource public function toArray($request) { $array = [ - 'id' => $this->id, - 'element_name' => $this->element_name, - 'element_id' => $this->element_id, - 'due_at' => $this->due_at, 'advancedStatus' => $this->advanceStatus, ]; + foreach (self::$defaultFields as $field) { + $array[$field] = $this->$field; + } return $this->processInclude($request, $array); } @@ -108,7 +109,7 @@ private static function addRelationship(ProcessRequestToken $model, string $rela } $relationshipColumns = self::$defaultFieldsFor[$relationship] ?? ['id']; - $model->$relationship = $relationshipObject->select($relationshipColumns)->get(); + $model->$relationship = $relationshipObject->select($relationshipColumns)->getResults(); return true; } @@ -127,6 +128,7 @@ public static function preprocessInclude(Request $request, Builder $query): self if (in_array('data', $include)) { $query->addSelect('process_request_id'); + self::$defaultFieldsFor['processRequest'][] = 'data'; } foreach (self::$includeMethods as $key) { diff --git a/ProcessMaker/Models/ScriptDockerNayraTrait.php b/ProcessMaker/Models/ScriptDockerNayraTrait.php index fc50e26e07..a840d22b32 100644 --- a/ProcessMaker/Models/ScriptDockerNayraTrait.php +++ b/ProcessMaker/Models/ScriptDockerNayraTrait.php @@ -2,10 +2,12 @@ namespace ProcessMaker\Models; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; use ProcessMaker\Exception\ScriptException; use ProcessMaker\Facades\Docker; +use ProcessMaker\ScriptRunners\Base; /** * Execute a docker container copying files to interchange information. @@ -36,12 +38,12 @@ public function handleNayraDocker(string $code, array $data, array $config, $tim $body = json_encode($params); $servers = Cache::get('nayra_ips'); if (!$servers) { - $url = config('app.nayra_rest_api_host') . '/run_script'; - } else { - $index = array_rand($servers); - $url = 'http://' . $servers[$index] . ':8080/run_script'; - $this->ensureNayraServerIsRunning('http://' . $servers[$index] . ':8080'); + $this->bringUpNayraContainer(); + $servers = Cache::get('nayra_ips'); } + $index = array_rand($servers); + $url = 'http://' . $servers[$index] . ':8080/run_script'; + $this->ensureNayraServerIsRunning('http://' . $servers[$index] . ':8080'); $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); @@ -104,6 +106,12 @@ private function bringUpNayra(string $url) $this->nayraServiceIsRunning($url); } + private function bringUpNayraContainer() + { + $lang = Base::NAYRA_LANG; + Artisan::call("processmaker:build-script-executor {$lang} --rebuild"); + } + /** * Waits for the container network to be ready. * diff --git a/ProcessMaker/Traits/TaskResourceIncludes.php b/ProcessMaker/Traits/TaskResourceIncludes.php index 467190244a..6c046cd905 100644 --- a/ProcessMaker/Traits/TaskResourceIncludes.php +++ b/ProcessMaker/Traits/TaskResourceIncludes.php @@ -135,7 +135,7 @@ private function includeProcess() return ['process' => $this->process]; } - private function includeInterstitial() + public function includeInterstitial() { $interstitial = $this->getInterstitial(); diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index df7e17a3bd..6c2d83307c 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -508,7 +508,7 @@ class="multiselect__tag-icon"> "COMPLETED": "open-style", "TRIGGERED": "open-style", }; - const status = this.task.advanceStatus.toUpperCase(); + const status = (this.task.advanceStatus || '').toUpperCase(); return "card-header text-status " + header[status]; }, isAllowReassignment() { @@ -646,9 +646,14 @@ class="multiselect__tag-icon"> // to view error details. This is done in loadTask in Task.vue if (error.response?.status && error.response?.status === 422) { // Validation error - Object.entries(error.response.data.errors).forEach(([key, value]) => { - window.ProcessMaker.alert(`${key}: ${value[0]}`, 'danger', 0); - }); + if (error.response.data.errors) { + Object.entries(error.response.data.errors).forEach(([key, value]) => { + window.ProcessMaker.alert(`${key}: ${value[0]}`, 'danger', 0); + }); + } else if (error.response.data.message) { + window.ProcessMaker.alert(error.response.data.message, 'danger', 0); + } + this.$refs.task.loadNextAssignedTask(); } }).finally(() => { this.submitting = false; diff --git a/routes/v1_1/api.php b/routes/v1_1/api.php index d4580ae996..fe1b6a01c7 100644 --- a/routes/v1_1/api.php +++ b/routes/v1_1/api.php @@ -21,5 +21,9 @@ // Route to show the screen of a task Route::get('/{taskId}/screen', [TaskController::class, 'showScreen']) ->name('show.screen'); + + // Route to show the interstitial screen of a task + Route::get('/{taskId}/interstitial', [TaskController::class, 'showInterstitial']) + ->name('show.interstitial'); }); });