chg: [workflow] Improved logging capabilities and stop aborting execution if non-blocking module return false

pull/8530/head
Sami Mokaddem 2022-06-24 11:22:26 +02:00
parent 1461f06638
commit a13c1a39e9
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
11 changed files with 87 additions and 72 deletions

View File

@ -52,6 +52,7 @@ class AuditLogsController extends AppController
'GalaxyClusterRelation',
'News',
'Warninglist',
'Workflow',
];
public $paginate = [

View File

@ -389,6 +389,7 @@ class LogsController extends AppController
'Galaxy',
'GalaxyCluster',
'GalaxyClusterRelation',
'Workflow',
];
sort($models);
$models = array('' => 'ALL') + $this->_arrayToValuesIndexArray($models);

View File

@ -114,7 +114,7 @@ class WorkflowsController extends AppController
]);
if (empty($savedWorkflow)) {
return $this->__getFailResponseBasedOnContext(
[__('Could not create workflow for trigger %s', $trigger_id), $this->validationErrors],
[__('Could not create workflow for trigger %s', $trigger_id), $this->Workflow->validationErrors],
null,
'add',
$trigger_id,

View File

@ -3422,10 +3422,11 @@ class AppModel extends Model
*
* @param string $trigger_id
* @param array $data Data to be passed to the workflow
* @param array $errors
* @param array $blockingErrors Errors will be appened if any
* @param array $logging If the execution failure should be logged
* @return boolean If the execution for the blocking path was a success
*/
public function executeTrigger($trigger_id, array $data=[], array &$blockingErrors=[]): bool
public function executeTrigger($trigger_id, array $data=[], array &$blockingErrors=[], array $logging=[]): bool
{
if ($this->Workflow === null) {
$this->Workflow = ClassRegistry::init('Workflow');
@ -3434,7 +3435,12 @@ class AppModel extends Model
$this->Workflow->checkTriggerEnabled($trigger_id) &&
$this->Workflow->checkTriggerListenedTo($trigger_id)
) {
return $this->Workflow->executeWorkflowForTrigger($trigger_id, $data, $blockingErrors);
$success = $this->Workflow->executeWorkflowForTrigger($trigger_id, $data, $blockingErrors);
if (!empty($logging)) {
$errorMessage = implode(', ', $blockingErrors);
$this->loadLog()->createLogEntry('SYSTEM', $logging['action'], $logging['model'], $logging['id'], $logging['message'], __('Returned message: %s', $errorMessage));
}
return $success;
}
return true;
}

View File

@ -4510,11 +4510,16 @@ class Event extends AppModel
'eventid' => $id,
'includeAttachments' => 1
]);
$errors = [];
$success = $this->executeTrigger('publish', $fullEvent[0], $errors);
$workflowErrors = [];
$logging = [
'model' => 'Event',
'action' => 'publish',
'id' => $id,
'message' => __('Publishing stopped by a blocking workflow.'),
];
$success = $this->executeTrigger('publish', $fullEvent[0], $workflowErrors, $logging);
if (empty($success)) {
$errorMessage = implode(', ', $errors);
$this->loadLog()->createLogEntry('SYSTEM', 'publish', 'Event', $id, __('Publishing stopped by a blocking workflow.'), __('Returned message: %s', $errorMessage));
$errorMessage = implode(', ', $workflowErrors);
return $errorMessage;
}
if ($jobId) {

View File

@ -137,11 +137,15 @@ class Workflow extends AppModel
protected function checkTriggerEnabled($trigger_id)
{
$filename = sprintf('Module_%s.php', preg_replace('/[^a-zA-Z0-9_]/', '_', Inflector::underscore($trigger_id)));
$module_config = $this->__getClassFromModuleFiles('trigger', [$filename])['classConfigs'];
$settingName = sprintf('Plugin.WorkflowTriggers_%s', $trigger_id);
$module_disabled = empty(Configure::read($settingName));
return empty($module_config['disabled']) && !$module_disabled;
if ($module_disabled) {
return false;
}
$filename = sprintf('Module_%s.php', preg_replace('/[^a-zA-Z0-9_]/', '_', Inflector::underscore($trigger_id)));
$module_config = $this->__getClassFromModuleFiles('trigger', [$filename])['classConfigs'];
return empty($module_config['disabled']);
}
protected function getEnabledModules(): array
@ -321,7 +325,7 @@ class Workflow extends AppModel
{
$graphData = !empty($workflow['Workflow']) ? $workflow['Workflow']['data'] : $workflow['data'];
$triggers = $this->workflowGraphTool->extractTriggersFromWorkflow($graphData, true);
return count($triggers) == 1;
return count($triggers) <= 1;
}
/**
@ -362,7 +366,7 @@ class Workflow extends AppModel
$blockingPathExecutionSuccess = $this->walkGraph($workflow, $startNode, Workflow::BLOCKING_PATH, $data, $blockingErrors, $walkResult);
if (empty($blockingPathExecutionSuccess)) {
$message = __('Error while executing blocking workflow. %s', PHP_EOL . implode(', ', $blockingErrors));
$this->Workflow->logExecutionError($workflow, $message);
$this->logExecutionError($workflow, $message);
}
return $blockingPathExecutionSuccess;
}
@ -426,6 +430,7 @@ class Workflow extends AppModel
$preventExecutionForPaths = [];
foreach ($graphWalker as $graphNode) {
$node = $graphNode['node'];
$moduleClass = $this->getModuleClass($node);
foreach ($preventExecutionForPaths as $path_to_block) {
if ($path_to_block == array_slice($graphNode['path_list'], 0, count($path_to_block))) {
$walkResult['blocked_paths'][] = $graphNode['path_list'];
@ -448,7 +453,9 @@ class Workflow extends AppModel
implode(', ', $nodeError)
);
}
return false; // Node stopped execution for blocking path
if (!empty($moduleClass->is_blocking)) {
return false; // Node stopped execution for blocking path
}
}
if ($graphNode['path_type'] == Workflow::NON_BLOCKING_PATH) {
$preventExecutionForPaths[] = $graphNode['path_list']; // Paths down the chain for this path should not be executed
@ -547,18 +554,6 @@ class Workflow extends AppModel
private function __mergeGlobalConfigIntoLoadedModules()
{
/* FIXME: Delete `disabled` entry. This is for testing while we wait for module settings */
// array_walk($this->loaded_modules['logic'], function (&$logic) {
// $module_enabled = true;
// $logic['disabled'] = !$module_enabled;
// $this->loaded_classes['logic'][$logic['id']]->disabled = $module_enabled;
// });
// array_walk($this->loaded_modules['action'], function (&$action) {
// $module_enabled = !in_array($action['id'], ['push-zmq', 'slack-message', 'mattermost-message', 'add-tag', 'writeactions', 'enrich-event', 'testaction', 'stop-execution', ]);
// $action['disabled'] = $module_enabled;
// $this->loaded_classes['action'][$action['id']]->disabled = $module_enabled;
// });
/* FIXME: end */
foreach ($this->loaded_modules['trigger'] as &$trigger) {
$module_disabled = empty(Configure::read(sprintf('Plugin.WorkflowTriggers_%s', $trigger['id'])));
$trigger['html_template'] = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
@ -670,8 +665,8 @@ class Workflow extends AppModel
$filepath = sprintf('%s%s/%s', Workflow::MODULE_ROOT_PATH, $type, $filename);
$instancedClass = $this->__getClassFromModuleFile($filepath);
if (is_string($instancedClass)) {
$message = sprintf(__('Error while trying to load module: %s'), $instancedClass);
$this->__logError($filename, $message);
$this->__logLoadingError($filename, $instancedClass);
continue;
}
if (!empty($classConfigs[$instancedClass->id])) {
throw new WorkflowDuplicatedModuleIDException(__('Module %s has already been defined', $instancedClass->id));
@ -691,11 +686,11 @@ class Workflow extends AppModel
$this->Log->createLogEntry('SYSTEM', 'execute_workflow', 'Workflow', $workflow['Workflow']['id'], $message);
}
private function __logError($id, $message)
private function __logLoadingError($filename, $error)
{
$this->Log = ClassRegistry::init('Log');
$this->Log->createLogEntry('SYSTEM', 'load_module', 'Workflow', $id, $message);
return false;
$message = __('Could not load module for file `%s`.', $filename);
$this->Log->createLogEntry('SYSTEM', 'load_module', 'Workflow', 0, $message, $error);
}
/**
@ -710,20 +705,26 @@ class Workflow extends AppModel
$className = explode('/', $filepath);
$className = str_replace('.php', '', $className[count($className)-1]);
try {
require_once($filepath);
if (!@include_once($filepath)) {
$message = __('Could not load module for path %s. File does not exists.', $filepath);
$this->log($message, LOG_ERR);
return $message;
}
try {
$reflection = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
$this->logException(__('Could not load module for path %s', $filepath), $e);
return false;
$message = __('Could not load module for path %s. Could not instanciate class', $filepath);
$this->logException($message, $e);
return $message;
}
$mainClass = $reflection->newInstance();
if ($mainClass->checkLoading() === 'The Factory Must Grow') {
return $mainClass;
}
} catch (Exception $e) {
$this->logException(__('Could not load module for path %s', $filepath), $e);
return false;
$message = __('Could not load module for path %s', $filepath);
$this->logException($message, $filepath, $e);
return $message;
}
}
@ -791,7 +792,7 @@ class Workflow extends AppModel
private function __incrementWorkflowExecutionCount(array $workflow): array
{
$workflow['Workflow']['counter'] = intval($workflow['Workflow']['counter']) + 1;
$this->Workflow->save($workflow, ['fieldList' => ['counter']]);
$this->save($workflow, ['fieldList' => ['counter']]);
return $this->fetchWorkflow($workflow['Workflow']['id']);
}

View File

@ -12,7 +12,6 @@ class Module_stop_execution extends WorkflowBaseModule
public $outputs = 1;
public $params = [];
public function __construct()
{
parent::__construct();

View File

@ -1,17 +0,0 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_user_add extends WorkflowBaseTriggerModule
{
public $id = 'user-add';
public $name = 'User Add';
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
public $icon = 'user-plus';
public $inputs = 0;
public $outputs = 1;
public function __construct()
{
parent::__construct();
}
}

View File

@ -0,0 +1,18 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_user_after_save extends WorkflowBaseTriggerModule
{
public $id = 'user-after-save';
public $name = 'User After Save';
public $description = 'This trigger is called after a user is saved in the database';
public $icon = 'user-edit';
public $inputs = 0;
public $outputs = 1;
public $canAbort = false;
public function __construct()
{
parent::__construct();
}
}

View File

@ -0,0 +1,18 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_user_before_save extends WorkflowBaseTriggerModule
{
public $id = 'user-before-save';
public $name = 'User Before Save';
public $description = 'This trigger is called just before a user is save in the database';
public $icon = 'user-plus';
public $inputs = 0;
public $outputs = 1;
public $canAbort = true;
public function __construct()
{
parent::__construct();
}
}

View File

@ -1,17 +0,0 @@
<?php
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
class Module_user_edit extends WorkflowBaseTriggerModule
{
public $id = 'user-edit';
public $name = 'User Edit';
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
public $icon = 'user-edit';
public $inputs = 0;
public $outputs = 1;
public function __construct()
{
parent::__construct();
}
}