From e5e6750f7c4582e3abab23ef72b5554783d7bc53 Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Tue, 24 Sep 2024 18:43:38 -0400 Subject: [PATCH 1/9] Implement script runner microservice Change class name of script microservice runner Add config for microservice ruuner Add callback route Remove send first response in TestScript Add version script in env file Move callback to api Implement script microservice in runscripttask.php Remove config of script-runners Adding type column to script executor table Change int for bool to sync variable fix composer.json file Fix api environment variables Fix script language capitalize Fix null version field Change output name in json callback Remove json_decode in output response Add script executor enabled flag Rollback _fonts.scss file --- ProcessMaker/Enums/ScriptExecutorType.php | 9 + ProcessMaker/Events/ScriptResponseEvent.php | 2 +- .../Http/Controllers/Api/ScriptController.php | 7 + .../Api/ScriptExecutorController.php | 1 + ProcessMaker/Jobs/RunScriptTask.php | 25 ++- ProcessMaker/Jobs/TestScript.php | 11 +- ProcessMaker/Models/Script.php | 6 +- ProcessMaker/Models/ScriptExecutor.php | 9 +- ProcessMaker/Models/ScriptExecutorVersion.php | 2 +- ProcessMaker/Models/ScriptVersion.php | 4 +- ProcessMaker/ScriptRunners/Base.php | 2 +- .../ScriptMicroserviceRunner.php | 165 ++++++++++++++++++ ProcessMaker/ScriptRunners/ScriptRunner.php | 39 +++-- .../Services/ScriptMicroserviceService.php | 41 +++++ config/script-runner-microservice.php | 16 ++ ...lumn_to_script_executor_versions_table.php | 27 +++ ..._type_column_to_script_executors_table.php | 27 +++ routes/api.php | 4 + 18 files changed, 365 insertions(+), 32 deletions(-) create mode 100644 ProcessMaker/Enums/ScriptExecutorType.php create mode 100644 ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php create mode 100644 ProcessMaker/Services/ScriptMicroserviceService.php create mode 100644 config/script-runner-microservice.php create mode 100644 database/migrations/2024_10_15_182703_add_type_column_to_script_executor_versions_table.php create mode 100644 database/migrations/2024_10_15_182703_add_type_column_to_script_executors_table.php diff --git a/ProcessMaker/Enums/ScriptExecutorType.php b/ProcessMaker/Enums/ScriptExecutorType.php new file mode 100644 index 0000000000..c59fe1e168 --- /dev/null +++ b/ProcessMaker/Enums/ScriptExecutorType.php @@ -0,0 +1,9 @@ +cacheResponse($this->response); + $response = $this->cacheResponse(); return [ 'type' => '.' . \get_class($this), diff --git a/ProcessMaker/Http/Controllers/Api/ScriptController.php b/ProcessMaker/Http/Controllers/Api/ScriptController.php index 7580b33bff..2e7fc5c5dc 100644 --- a/ProcessMaker/Http/Controllers/Api/ScriptController.php +++ b/ProcessMaker/Http/Controllers/Api/ScriptController.php @@ -18,6 +18,7 @@ use ProcessMaker\Models\ScriptCategory; use ProcessMaker\Models\User; use ProcessMaker\Query\SyntaxError; +use ProcessMaker\Services\ScriptMicroserviceService; use ProcessMaker\Traits\ProjectAssetTrait; class ScriptController extends Controller @@ -286,6 +287,12 @@ public function execution($key) return response()->json(Cache::get("srn.{$key}")); } + public function microserviceExecution(Request $request) + { + $scriptMicroserviceService = new ScriptMicroserviceService(); + $scriptMicroserviceService->handle($request); + } + /** * Get a single script in a process. * diff --git a/ProcessMaker/Http/Controllers/Api/ScriptExecutorController.php b/ProcessMaker/Http/Controllers/Api/ScriptExecutorController.php index e625ac01d8..118e1301e7 100644 --- a/ProcessMaker/Http/Controllers/Api/ScriptExecutorController.php +++ b/ProcessMaker/Http/Controllers/Api/ScriptExecutorController.php @@ -103,6 +103,7 @@ public function index(Request $request) */ public function store(Request $request) { + $request->request->add(['type' => 'custom']); $this->checkAuth($request); $request->validate(ScriptExecutor::rules()); diff --git a/ProcessMaker/Jobs/RunScriptTask.php b/ProcessMaker/Jobs/RunScriptTask.php index 1d4c7a632c..abc9b54194 100644 --- a/ProcessMaker/Jobs/RunScriptTask.php +++ b/ProcessMaker/Jobs/RunScriptTask.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Facades\Log; +use ProcessMaker\Enums\ScriptExecutorType; use ProcessMaker\Exception\ConfigurationException; use ProcessMaker\Exception\ScriptException; use ProcessMaker\Facades\WorkflowManager; @@ -32,9 +33,9 @@ class RunScriptTask extends BpmnAction implements ShouldQueue /** * Create a new job instance. * - * @param \ProcessMaker\Models\Process $definitions - * @param \ProcessMaker\Models\ProcessRequest $instance - * @param \ProcessMaker\Models\ProcessRequestToken $token + * @param Definitions $definitions + * @param ProcessRequest $instance + * @param ProcessRequestToken $token * @param array $data */ public function __construct(Definitions $definitions, ProcessRequest $instance, ProcessRequestToken $token, array $data, $attemptNum = 1) @@ -68,6 +69,7 @@ public function action(ProcessRequestToken $token = null, ScriptTaskInterface $e } $errorHandling = null; + $scriptExecutor = null; try { if (empty($scriptRef)) { $code = $element->getScript(); @@ -86,6 +88,7 @@ public function action(ProcessRequestToken $token = null, ScriptTaskInterface $e if (!$script) { throw new ConfigurationException(__('Script ":id" not found', ['id' => $scriptRef])); } + $scriptExecutor = $script->scriptExecutor; $script = $script->versionFor($instance); } @@ -95,9 +98,19 @@ public function action(ProcessRequestToken $token = null, ScriptTaskInterface $e $this->unlock(); $dataManager = new DataManager(); $data = $dataManager->getData($token); - $response = $script->runScript($data, $configuration, $token->getId(), $errorHandling->timeout()); - - $this->updateData($response); + $metadata = [ + 'script_task' => [ + 'definition_id' => $this->definitionsId, + 'instance_id' => $this->instanceId, + 'token_id' => $this->tokenId, + ], + ]; + $response = $script->runScript($data, $configuration, $token->getId(), $errorHandling->timeout(), 0, $metadata); + + if (!config('script-runner-microservice.enabled') || + ($scriptExecutor && $scriptExecutor->type === ScriptExecutorType::Custom)) { + $this->updateData($response); + } } catch (ConfigurationException $exception) { $this->unlock(); $this->updateData(['output' => $exception->getMessageForData($token)]); diff --git a/ProcessMaker/Jobs/TestScript.php b/ProcessMaker/Jobs/TestScript.php index 0e9b2b3fe9..29f8e8363a 100644 --- a/ProcessMaker/Jobs/TestScript.php +++ b/ProcessMaker/Jobs/TestScript.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use ProcessMaker\Enums\ScriptExecutorType; use ProcessMaker\Events\ScriptResponseEvent; use ProcessMaker\Models\Script; use ProcessMaker\Models\User; @@ -60,8 +61,14 @@ public function handle() try { // Just set the code but do not save the object (preview only) $this->script->code = $this->code; - $response = $this->script->runScript($this->data, $this->configuration); - $this->sendResponse(200, $response); + $this->script->nonce = $this->nonce; + $response = $this->script->runScript($this->data, $this->configuration, '', null, 0); + \Log::debug('Response api microservice: ' . print_r($response, true)); + + if (!config('script-runner-microservice.enabled') || + $this->script->scriptExecutor && $this->script->scriptExecutor->type === ScriptExecutorType::Custom) { + $this->sendResponse(200, $response); + } } catch (Throwable $exception) { $this->sendResponse(500, [ 'exception' => get_class($exception), diff --git a/ProcessMaker/Models/Script.php b/ProcessMaker/Models/Script.php index 8b900db30e..31c71ddaae 100644 --- a/ProcessMaker/Models/Script.php +++ b/ProcessMaker/Models/Script.php @@ -149,7 +149,7 @@ public static function rules($existing = null) * @param array $data * @param array $config */ - public function runScript(array $data, array $config, $tokenId = '', $timeout = null) + public function runScript(array $data, array $config, $tokenId = '', $timeout = null, $sync = 1, $metadata = []) { if (!$timeout) { $timeout = $this->timeout; @@ -158,14 +158,14 @@ public function runScript(array $data, array $config, $tokenId = '', $timeout = if (!$this->scriptExecutor) { throw new ScriptLanguageNotSupported($this->language); } - $runner = new ScriptRunner($this->scriptExecutor); + $runner = new ScriptRunner($this); $runner->setTokenId($tokenId); $user = User::find($this->run_as_user_id); if (!$user) { throw new ConfigurationException('A user is required to run scripts'); } - return $runner->run($this->code, $data, $config, $timeout, $user); + return $runner->run($this->code, $data, $config, $timeout, $user, $sync, $metadata); } /** diff --git a/ProcessMaker/Models/ScriptExecutor.php b/ProcessMaker/Models/ScriptExecutor.php index f5d8b34346..64ad0cee7f 100644 --- a/ProcessMaker/Models/ScriptExecutor.php +++ b/ProcessMaker/Models/ScriptExecutor.php @@ -4,6 +4,8 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Enum; +use ProcessMaker\Enums\ScriptExecutorType; use ProcessMaker\Exception\ScriptLanguageNotSupported; use ProcessMaker\Facades\Docker; use ProcessMaker\Traits\Exportable; @@ -56,7 +58,7 @@ class ScriptExecutor extends ProcessMakerModel use HideSystemResources; protected $fillable = [ - 'title', 'description', 'language', 'config', 'is_system', + 'title', 'description', 'language', 'config', 'is_system', 'type', ]; public static function install($params) @@ -146,6 +148,11 @@ public static function rules($existing = null) 'required', Rule::in(Script::scriptFormatValues()), ], + 'type' => [ + 'sometimes', + new Enum(ScriptExecutorType::class), + 'nullable', + ], ]; } diff --git a/ProcessMaker/Models/ScriptExecutorVersion.php b/ProcessMaker/Models/ScriptExecutorVersion.php index e3ae78b721..076952bb6e 100644 --- a/ProcessMaker/Models/ScriptExecutorVersion.php +++ b/ProcessMaker/Models/ScriptExecutorVersion.php @@ -7,7 +7,7 @@ class ScriptExecutorVersion extends ProcessMakerModel { protected $fillable = [ - 'title', 'description', 'language', 'config', 'draft', 'is_system', + 'title', 'description', 'language', 'config', 'draft', 'is_system', 'type', ]; /** diff --git a/ProcessMaker/Models/ScriptVersion.php b/ProcessMaker/Models/ScriptVersion.php index d1c946b24c..d6b8e9e838 100644 --- a/ProcessMaker/Models/ScriptVersion.php +++ b/ProcessMaker/Models/ScriptVersion.php @@ -50,7 +50,7 @@ public function parent() * @param array $data * @param array $config */ - public function runScript(array $data, array $config, $tokenId = '', $timeout = null) + public function runScript(array $data, array $config, $tokenId = '', $timeout = null, $sync = 1, $metadata = []) { $script = $this->parent->replicate(); $except = $script->getGuarded(); @@ -58,7 +58,7 @@ public function runScript(array $data, array $config, $tokenId = '', $timeout = $script->$prop = $this->$prop; } - return $script->runScript($data, $config, $tokenId, $timeout); + return $script->runScript($data, $config, $tokenId, $timeout, $sync, $metadata); } /** diff --git a/ProcessMaker/ScriptRunners/Base.php b/ProcessMaker/ScriptRunners/Base.php index 0db1bb0511..c609f3a0d6 100644 --- a/ProcessMaker/ScriptRunners/Base.php +++ b/ProcessMaker/ScriptRunners/Base.php @@ -65,7 +65,7 @@ public function __construct(ScriptExecutor $scriptExecutor) * @return array * @throws RuntimeException */ - public function run($code, array $data, array $config, $timeout, ?User $user) + public function run($code, array $data, array $config, $timeout, ?User $user, $sync, $metadata) { $isNayra = $this->scriptExecutor->language === self::NAYRA_LANG; diff --git a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php new file mode 100644 index 0000000000..420405652b --- /dev/null +++ b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php @@ -0,0 +1,165 @@ +language = strtolower($script->language ?? $script->scriptExecutor->language); + } + + public function getAccessToken() + { + if (Cache::has('keycloak.access_token')) { + return Cache::get('keycloak.access_token'); + } + + $response = Http::asForm()->post(config('script-runner-microservice.keycloak.base_url'), [ + 'grant_type' => 'password', + 'client_id' => config('script-runner-microservice.keycloak.client_id'), + 'client_secret' => config('script-runner-microservice.keycloak.client_secret'), + 'username' => config('script-runner-microservice.keycloak.username'), + 'password' => config('script-runner-microservice.keycloak.password'), + ]); + + if ($response->successful()) { + Cache::put('keycloak.access_token', $response->json()['access_token'], $response->json()['expires_in'] - 60); + } + + return Cache::get('keycloak.access_token'); + } + + public function getScriptRunner() + { + $response = Cache::remember('script-runner-microservice.script-languages', now()->addDay(), function () { + return Http::withToken($this->getAccessToken()) + ->get(config('script-runner-microservice.base_url') . '/scripts')->collect(); + }); + + return $response->filter(function ($item) { + return $item['language'] == $this->language; + })->first(); + } + + public function run($code, array $data, array $config, $timeout, $user, $sync, $metadata) + { + Log::debug('Language: ' . $this->language); + Log::debug('Sync: ' . $sync); + Log::debug('Metadata: ' . print_r($metadata, true)); + + $scriptRunner = $this->getScriptRunner(); + + if (!$scriptRunner) { + throw new ConfigurationException('No exists script executor for this language: ' . $this->language); + } + $metadata = array_merge($this->getMetadata($user), $metadata); + + $payload = [ + 'version' => config('script-runner-microservice.version') ?? $this->getProcessMakerVersion(), + 'language' => $scriptRunner['language'], + 'metadata'=> $metadata, + 'data' => !empty($data) ? $this->sanitizeCss($data) : new stdClass(), + 'config' => !empty($config) ? $config : new stdClass(), + 'script' => base64_encode(str_replace("'", ''', $code)), + 'secrets' => $this->getEnvironmentVariables($user), + 'callback' => config('script-runner-microservice.callback'), + 'debug' => true, + 'timeout' => $timeout, + 'sync' => $sync, + ]; + + Log::debug(print_r($payload, true)); + + $response = Http::withToken($this->getAccessToken()) + ->post(config('script-runner-microservice.base_url') . '/requests/create', $payload); + + $response->throw(); + + return $response->json(); + } + + private function getEnvironmentVariables(User $user) + { + $variablesParameter = []; + EnvironmentVariable::chunk(50, function (Collection $variables) use (&$variablesParameter) { + foreach ($variables as $variable) { + // Fix variables that have spaces + $variablesParameter[str_replace(' ', '_', $variable->name)] = $variable->value; + } + }); + + // Add the url to the host + $variablesParameter['HOST_URL'] = config('app.docker_host_url'); + + // Create tokens for the SDK if a user is set + $token = null; + if ($user) { + $accessToken = Cache::remember('script-runner-' . $user->id, now()->addWeek(), function () use ($user) { + $user->removeOldRunScriptTokens(); + $token = new GenerateAccessToken($user); + + return $token->getToken(); + }); + $variablesParameter['API_TOKEN'] = $accessToken; + $variablesParameter['API_HOST'] = config('app.docker_host_url') . '/api/1.0'; + $variablesParameter['APP_URL'] = config('app.docker_host_url'); + $variablesParameter['API_SSL_VERIFY'] = (config('app.api_ssl_verify') ? '1' : '0'); + } + + return $variablesParameter; + } + + public function setTokenId($tokenId) + { + $this->tokenId = $tokenId; + } + + public function getProcessMakerVersion() + { + return Cache::remember('script-runner-microservice.processmaker-version', now()->addDay(), function () { + $composer_json_path = json_decode(file_get_contents(base_path() . '/composer.json')); + + return $composer_json_path->version; + }); + } + + public function getMetadata($user) + { + return [ + 'nonce' => $this->script->nonce, + 'script_id' => $this->script->id, + 'instance' => config('app.url'), + 'user_id' => $user->id, + 'user_email' => $user->email, + ]; + } + + public function sanitizeCss($data) + { + if ($this->language !== 'javascript-ssr') { + return $data; + } + if (array_key_exists('css', $data)) { + $data['css'] = false; + } + + return $data; + } +} diff --git a/ProcessMaker/ScriptRunners/ScriptRunner.php b/ProcessMaker/ScriptRunners/ScriptRunner.php index 97b4ae878d..0e59e4be61 100644 --- a/ProcessMaker/ScriptRunners/ScriptRunner.php +++ b/ProcessMaker/ScriptRunners/ScriptRunner.php @@ -2,7 +2,10 @@ namespace ProcessMaker\ScriptRunners; +use Illuminate\Contracts\Container\BindingResolutionException; +use ProcessMaker\Enums\ScriptExecutorType; use ProcessMaker\Exception\ScriptLanguageNotSupported; +use ProcessMaker\Models\Script; use ProcessMaker\Models\ScriptExecutor; class ScriptRunner @@ -10,13 +13,13 @@ class ScriptRunner /** * Concrete script runner * - * @var \ProcessMaker\ScriptRunners\Base + * @var Base */ private $runner; - public function __construct(ScriptExecutor $executor) + public function __construct(protected Script $script) { - $this->runner = $this->getScriptRunner($executor); + $this->runner = $this->getScriptRunner($this->script->scriptExecutor); } /** @@ -31,9 +34,9 @@ public function __construct(ScriptExecutor $executor) * @return array * @throws \RuntimeException */ - public function run($code, array $data, array $config, $timeout, $user) + public function run($code, array $data, array $config, $timeout, $user, $sync, $metadata) { - return $this->runner->run($code, $data, $config, $timeout, $user); + return $this->runner->run($code, $data, $config, $timeout, $user, $sync, $metadata); } /** @@ -41,19 +44,25 @@ public function run($code, array $data, array $config, $timeout, $user) * * @param ScriptExecutor $executor * - * @return \ProcessMaker\ScriptRunners\Base - * @throws \ProcessMaker\Exception\ScriptLanguageNotSupported + * @return Base|ScriptMicroserviceRunner + * @throws ScriptLanguageNotSupported + * @throws BindingResolutionException */ - private function getScriptRunner(ScriptExecutor $executor) + private function getScriptRunner(ScriptExecutor $executor): Base|ScriptMicroserviceRunner { - $language = strtolower($executor->language); - $runner = config("script-runners.{$language}.runner"); - if (!$runner) { - throw new ScriptLanguageNotSupported($language); - } else { - $class = "ProcessMaker\\ScriptRunners\\{$runner}"; + // Todo compare if executor is custom $executor->type = 'custom' + if (!config('script-runner-microservice.enabled') || $executor->type === ScriptExecutorType::Custom) { + $language = strtolower($executor->language); + $runner = config("script-runners.{$language}.runner"); + if (!$runner) { + throw new ScriptLanguageNotSupported($language); + } else { + $class = "ProcessMaker\\ScriptRunners\\{$runner}"; - return app()->make($class, ['scriptExecutor' => $executor]); + return app()->make($class, ['scriptExecutor' => $executor]); + } + } else { + return new ScriptMicroserviceRunner($this->script); } } diff --git a/ProcessMaker/Services/ScriptMicroserviceService.php b/ProcessMaker/Services/ScriptMicroserviceService.php new file mode 100644 index 0000000000..f79686e4d9 --- /dev/null +++ b/ProcessMaker/Services/ScriptMicroserviceService.php @@ -0,0 +1,41 @@ +all(); + Log::debug('Response microservice executor: ' . print_r($response, true)); + // If the call is from preview + if ($response['metadata']['nonce']) { + $status = $response['status'] === 'success' ? 200 : 500; + $output = $response['status'] === 'success' + ? ['output' => $response['output']] + : $response; + + event(new ScriptResponseEvent( + User::find($response['metadata']['user_id']), + $status, + $output, + null, + $response['metadata']['nonce'])); + } + if (!empty($response['metadata']['script_task']) && $response['status'] === 'success') { + $definitions = Definitions::find($response['metadata']['script_task']['definition_id']); + $instance = ProcessRequest::find($response['metadata']['script_task']['instance_id']); + $token = ProcessRequestToken::find($response['metadata']['script_task']['token_id']); + CompleteActivity::dispatch($definitions, $instance, $token, $response['output'])->onQueue('bpmn'); + } + } +} diff --git a/config/script-runner-microservice.php b/config/script-runner-microservice.php new file mode 100644 index 0000000000..570a73ace2 --- /dev/null +++ b/config/script-runner-microservice.php @@ -0,0 +1,16 @@ + env('SCRIPT_MICROSERVICE_ENABLED', true), + 'base_url' => env('SCRIPT_MICROSERVICE_BASE_URL'), + 'callback' => env('SCRIPT_MICROSERVICE_CALLBACK'), + 'version' => env('SCRIPT_MICROSERVICE_VERSION'), + 'keycloak' => [ + 'client_id' => env('KEYCLOAK_CLIENT_ID'), + 'client_secret' => env('KEYCLOAK_CLIENT_SECRET'), + 'redirect' => env('KEYCLOAK_REDIRECT_URI'), + 'base_url' => env('KEYCLOAK_BASE_URL'), + 'username' => env('KEYCLOAK_USERNAME'), + 'password' => env('KEYCLOAK_PASSWORD'), + ], +]; diff --git a/database/migrations/2024_10_15_182703_add_type_column_to_script_executor_versions_table.php b/database/migrations/2024_10_15_182703_add_type_column_to_script_executor_versions_table.php new file mode 100644 index 0000000000..8654582762 --- /dev/null +++ b/database/migrations/2024_10_15_182703_add_type_column_to_script_executor_versions_table.php @@ -0,0 +1,27 @@ +string('type')->nullable()->after('config'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('script_executor_versions', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } +}; diff --git a/database/migrations/2024_10_15_182703_add_type_column_to_script_executors_table.php b/database/migrations/2024_10_15_182703_add_type_column_to_script_executors_table.php new file mode 100644 index 0000000000..37ec329a7d --- /dev/null +++ b/database/migrations/2024_10_15_182703_add_type_column_to_script_executors_table.php @@ -0,0 +1,27 @@ +string('type')->nullable()->after('is_system'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('script_executors', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } +}; diff --git a/routes/api.php b/routes/api.php index 4bc4f33a5f..110b94e4e3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -411,3 +411,7 @@ Route::get('devlink/{devLink}', [DevLinkController::class, 'show'])->name('devlink.show'); }); }); + +Route::prefix('api')->name('api.')->group(function () { + Route::post('/scripts/microservice/execution', [ScriptController::class, 'microserviceExecution']); +}); From b5bb7d64f46754a2a630b45dc2efa46de5ede6db Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Wed, 15 Jan 2025 19:09:13 -0400 Subject: [PATCH 2/9] Remove Todo comment, add more information in metadata array --- ProcessMaker/Jobs/RunScriptTask.php | 3 +++ ProcessMaker/ScriptRunners/ScriptRunner.php | 1 - ProcessMaker/Services/ScriptMicroserviceService.php | 8 ++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ProcessMaker/Jobs/RunScriptTask.php b/ProcessMaker/Jobs/RunScriptTask.php index abc9b54194..5558c23fa5 100644 --- a/ProcessMaker/Jobs/RunScriptTask.php +++ b/ProcessMaker/Jobs/RunScriptTask.php @@ -100,9 +100,12 @@ public function action(ProcessRequestToken $token = null, ScriptTaskInterface $e $data = $dataManager->getData($token); $metadata = [ 'script_task' => [ + 'script_id' => $scriptRef, 'definition_id' => $this->definitionsId, 'instance_id' => $this->instanceId, 'token_id' => $this->tokenId, + 'data' => $data, + 'attempts' => $this->attemptNum, ], ]; $response = $script->runScript($data, $configuration, $token->getId(), $errorHandling->timeout(), 0, $metadata); diff --git a/ProcessMaker/ScriptRunners/ScriptRunner.php b/ProcessMaker/ScriptRunners/ScriptRunner.php index 0e59e4be61..e031b36e6d 100644 --- a/ProcessMaker/ScriptRunners/ScriptRunner.php +++ b/ProcessMaker/ScriptRunners/ScriptRunner.php @@ -50,7 +50,6 @@ public function run($code, array $data, array $config, $timeout, $user, $sync, $ */ private function getScriptRunner(ScriptExecutor $executor): Base|ScriptMicroserviceRunner { - // Todo compare if executor is custom $executor->type = 'custom' if (!config('script-runner-microservice.enabled') || $executor->type === ScriptExecutorType::Custom) { $language = strtolower($executor->language); $runner = config("script-runners.{$language}.runner"); diff --git a/ProcessMaker/Services/ScriptMicroserviceService.php b/ProcessMaker/Services/ScriptMicroserviceService.php index f79686e4d9..a7f07309be 100644 --- a/ProcessMaker/Services/ScriptMicroserviceService.php +++ b/ProcessMaker/Services/ScriptMicroserviceService.php @@ -9,6 +9,7 @@ use ProcessMaker\Models\Process as Definitions; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Models\ProcessRequestToken; +use ProcessMaker\Models\Script; use ProcessMaker\Models\User; class ScriptMicroserviceService @@ -31,11 +32,14 @@ public function handle(Request $request) null, $response['metadata']['nonce'])); } - if (!empty($response['metadata']['script_task']) && $response['status'] === 'success') { + if (!empty($response['metadata']['script_task'])) { + $script = Script::find($response['metadata']['script_task']['script_id']); $definitions = Definitions::find($response['metadata']['script_task']['definition_id']); $instance = ProcessRequest::find($response['metadata']['script_task']['instance_id']); $token = ProcessRequestToken::find($response['metadata']['script_task']['token_id']); - CompleteActivity::dispatch($definitions, $instance, $token, $response['output'])->onQueue('bpmn'); + if ($response['status'] === 'success') { + CompleteActivity::dispatch($definitions, $instance, $token, $response['output'])->onQueue('bpmn'); + } } } } From 5199799a31d9574f3a66d3ce7ef07eff3f25d589 Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Tue, 21 Jan 2025 22:14:21 -0400 Subject: [PATCH 3/9] Fix current user in TestScript --- ProcessMaker/Jobs/TestScript.php | 7 +++++-- ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php | 1 - ProcessMaker/Services/ScriptMicroserviceService.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ProcessMaker/Jobs/TestScript.php b/ProcessMaker/Jobs/TestScript.php index 29f8e8363a..c4055e6e1f 100644 --- a/ProcessMaker/Jobs/TestScript.php +++ b/ProcessMaker/Jobs/TestScript.php @@ -61,8 +61,11 @@ public function handle() try { // Just set the code but do not save the object (preview only) $this->script->code = $this->code; - $this->script->nonce = $this->nonce; - $response = $this->script->runScript($this->data, $this->configuration, '', null, 0); + $metadata = [ + 'nonce' => $this->nonce, + 'current_user' => $this->current_user, + ]; + $response = $this->script->runScript($this->data, $this->configuration, '', null, 0, $metadata); \Log::debug('Response api microservice: ' . print_r($response, true)); if (!config('script-runner-microservice.enabled') || diff --git a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php index 420405652b..11ee8803de 100644 --- a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php +++ b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php @@ -143,7 +143,6 @@ public function getProcessMakerVersion() public function getMetadata($user) { return [ - 'nonce' => $this->script->nonce, 'script_id' => $this->script->id, 'instance' => config('app.url'), 'user_id' => $user->id, diff --git a/ProcessMaker/Services/ScriptMicroserviceService.php b/ProcessMaker/Services/ScriptMicroserviceService.php index a7f07309be..0cb7328bd0 100644 --- a/ProcessMaker/Services/ScriptMicroserviceService.php +++ b/ProcessMaker/Services/ScriptMicroserviceService.php @@ -26,7 +26,7 @@ public function handle(Request $request) : $response; event(new ScriptResponseEvent( - User::find($response['metadata']['user_id']), + User::find($response['metadata']['current_user']), $status, $output, null, From 9e243901d821ec06dfced4e5b9c18d5c4aa4bd62 Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Tue, 21 Jan 2025 23:20:41 -0400 Subject: [PATCH 4/9] Fix issue whit current user model --- ProcessMaker/Jobs/TestScript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessMaker/Jobs/TestScript.php b/ProcessMaker/Jobs/TestScript.php index c4055e6e1f..23c56de091 100644 --- a/ProcessMaker/Jobs/TestScript.php +++ b/ProcessMaker/Jobs/TestScript.php @@ -63,7 +63,7 @@ public function handle() $this->script->code = $this->code; $metadata = [ 'nonce' => $this->nonce, - 'current_user' => $this->current_user, + 'current_user' => $this->current_user?->id, ]; $response = $this->script->runScript($this->data, $this->configuration, '', null, 0, $metadata); \Log::debug('Response api microservice: ' . print_r($response, true)); From 62de2114c3310b5c932f1353e1b81028622095d5 Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Tue, 21 Jan 2025 23:21:39 -0400 Subject: [PATCH 5/9] Remove test unit for lua script runner --- tests/Feature/Api/ScriptsTest.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/Feature/Api/ScriptsTest.php b/tests/Feature/Api/ScriptsTest.php index e14f14a78e..bb76dad7db 100644 --- a/tests/Feature/Api/ScriptsTest.php +++ b/tests/Feature/Api/ScriptsTest.php @@ -379,16 +379,6 @@ public function testPreviewScript() ScriptResponseEvent::class, ]); - $url = route('api.scripts.preview', $this->getScript('lua')->id); - $response = $this->apiCall('POST', $url, ['data' => '{}', 'code' => 'return {response=1}']); - $response->assertStatus(200); - Event::assertDispatched(ScriptResponseEvent::class, function ($event) { - $response = $event->response; - $nonce = $event->nonce; - - return $response['output'] === ['response' => 1]; - }); - $url = route('api.scripts.preview', $this->getScript('php')->id); $response = $this->apiCall('POST', $url, [ 'data' => '{}', From dc1b85ae0e6c12680c62cdb16e8c4f24e65001bd Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Thu, 23 Jan 2025 09:37:30 -0400 Subject: [PATCH 6/9] Add callback security --- ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php index 11ee8803de..dec5066f9a 100644 --- a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php +++ b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php @@ -80,12 +80,14 @@ public function run($code, array $data, array $config, $timeout, $user, $sync, $ 'script' => base64_encode(str_replace("'", ''', $code)), 'secrets' => $this->getEnvironmentVariables($user), 'callback' => config('script-runner-microservice.callback'), + 'callback_secure' => true, + 'callback_token' => 'API_TOKEN', 'debug' => true, 'timeout' => $timeout, 'sync' => $sync, ]; - Log::debug(print_r($payload, true)); + Log::debug('Payload: ' . print_r($payload, true)); $response = Http::withToken($this->getAccessToken()) ->post(config('script-runner-microservice.base_url') . '/requests/create', $payload); From 2a06185cc50b34fcfb6fd907c70757936bc9633f Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Thu, 23 Jan 2025 10:38:05 -0400 Subject: [PATCH 7/9] Set script microservice false for unit tests --- phpunit.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpunit.xml b/phpunit.xml index 231376cc93..fade58e673 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -76,5 +76,8 @@ + + + From df43b191da0e7b8378afd7169f09a8935e59835f Mon Sep 17 00:00:00 2001 From: Gustavo Silva Saire Date: Thu, 23 Jan 2025 12:11:04 -0400 Subject: [PATCH 8/9] Fix phpunit issues --- ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php | 5 +++-- ProcessMaker/ScriptRunners/ScriptRunner.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php index dec5066f9a..26b3caa32f 100644 --- a/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php +++ b/ProcessMaker/ScriptRunners/ScriptMicroserviceRunner.php @@ -70,6 +70,7 @@ public function run($code, array $data, array $config, $timeout, $user, $sync, $ throw new ConfigurationException('No exists script executor for this language: ' . $this->language); } $metadata = array_merge($this->getMetadata($user), $metadata); + $environmentVariables = $this->getEnvironmentVariables($user); $payload = [ 'version' => config('script-runner-microservice.version') ?? $this->getProcessMakerVersion(), @@ -78,10 +79,10 @@ public function run($code, array $data, array $config, $timeout, $user, $sync, $ 'data' => !empty($data) ? $this->sanitizeCss($data) : new stdClass(), 'config' => !empty($config) ? $config : new stdClass(), 'script' => base64_encode(str_replace("'", ''', $code)), - 'secrets' => $this->getEnvironmentVariables($user), + 'secrets' => $environmentVariables, 'callback' => config('script-runner-microservice.callback'), 'callback_secure' => true, - 'callback_token' => 'API_TOKEN', + 'callback_token' => $environmentVariables['API_TOKEN'], 'debug' => true, 'timeout' => $timeout, 'sync' => $sync, diff --git a/ProcessMaker/ScriptRunners/ScriptRunner.php b/ProcessMaker/ScriptRunners/ScriptRunner.php index e031b36e6d..e40795192c 100644 --- a/ProcessMaker/ScriptRunners/ScriptRunner.php +++ b/ProcessMaker/ScriptRunners/ScriptRunner.php @@ -44,11 +44,11 @@ public function run($code, array $data, array $config, $timeout, $user, $sync, $ * * @param ScriptExecutor $executor * - * @return Base|ScriptMicroserviceRunner + * @return Base|ScriptMicroserviceRunner|MockRunner * @throws ScriptLanguageNotSupported * @throws BindingResolutionException */ - private function getScriptRunner(ScriptExecutor $executor): Base|ScriptMicroserviceRunner + private function getScriptRunner(ScriptExecutor $executor): Base|ScriptMicroserviceRunner|MockRunner { if (!config('script-runner-microservice.enabled') || $executor->type === ScriptExecutorType::Custom) { $language = strtolower($executor->language); From a884f2beee7369aefa64fe62c21a89e12781955d Mon Sep 17 00:00:00 2001 From: Gustavo Silva Date: Fri, 24 Jan 2025 12:30:23 -0400 Subject: [PATCH 9/9] Move microservice api to protected route --- routes/api.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/routes/api.php b/routes/api.php index 110b94e4e3..56151c5420 100644 --- a/routes/api.php +++ b/routes/api.php @@ -128,6 +128,7 @@ Route::post('scripts/{script}/preview', [ScriptController::class, 'preview'])->name('scripts.preview')->middleware('can:view-scripts,script'); Route::post('scripts/execute/{script_id}/{script_key?}', [ScriptController::class, 'execute'])->name('scripts.execute'); Route::get('scripts/execution/{key}', [ScriptController::class, 'execution'])->name('scripts.execution'); + Route::post('scripts/microservice/execution', [ScriptController::class, 'microserviceExecution']); // Script Categories Route::get('script_categories', [ScriptCategoryController::class, 'index'])->name('script_categories.index')->middleware('can:view-script-categories'); @@ -411,7 +412,3 @@ Route::get('devlink/{devLink}', [DevLinkController::class, 'show'])->name('devlink.show'); }); }); - -Route::prefix('api')->name('api.')->group(function () { - Route::post('/scripts/microservice/execution', [ScriptController::class, 'microserviceExecution']); -});