From 57c0eb8c1ff048cf9f5f30b88ff8aceec2133f9d Mon Sep 17 00:00:00 2001 From: Peter Burnett Date: Sat, 2 Mar 2024 14:49:34 +1000 Subject: [PATCH] feat: Add notification pathway for aborted flows --- classes/dataflow.php | 1 + classes/form/dataflow_form.php | 3 ++ classes/local/execution/engine.php | 45 ++++++++++++++++++++++++++++++ db/install.xml | 1 + db/upgrade.php | 15 ++++++++++ lang/en/tool_dataflows.php | 8 ++++++ version.php | 4 +-- 7 files changed, 75 insertions(+), 2 deletions(-) diff --git a/classes/dataflow.php b/classes/dataflow.php index c54dee13..57ef72db 100644 --- a/classes/dataflow.php +++ b/classes/dataflow.php @@ -63,6 +63,7 @@ protected static function define_properties(): array { 'timemodified' => ['type' => PARAM_INT, 'default' => 0], 'usermodified' => ['type' => PARAM_INT, 'default' => 0], 'confighash' => ['type' => PARAM_TEXT, 'default' => ''], + 'notifyonabort' => ['type' => PARAM_TEXT, 'default' => ''] ]; } diff --git a/classes/form/dataflow_form.php b/classes/form/dataflow_form.php index c901fb34..87995946 100644 --- a/classes/form/dataflow_form.php +++ b/classes/form/dataflow_form.php @@ -116,6 +116,9 @@ public function definition() { $select->setMultiple(true); $mform->addElement('static', 'log_handlers_desc', '', get_string('log_handlers_desc', 'tool_dataflows')); + $mform->addElement('text', 'notifyonabort', get_string('notifyonabort', 'tool_dataflows')); + $mform->addElement('static', 'notifyonabort_desc', '', get_string('notifyonabort_desc', 'tool_dataflows')); + $this->add_action_buttons(); } diff --git a/classes/local/execution/engine.php b/classes/local/execution/engine.php index 15de137d..85d52752 100644 --- a/classes/local/execution/engine.php +++ b/classes/local/execution/engine.php @@ -369,6 +369,10 @@ public function initialise() { $error = error_get_last(); $this->logger->log(Logger::ERROR, 'Engine: shutdown happened abruptly', ['lasterror' => $error]); $this->set_status(self::STATUS_ABORTED); + + $notifyreason = new \Exception('Shutdown handler triggered abort. Last error: ' . $error); + $this->notify_on_abort($notifyreason); + $this->run->finalise($this->status, $this->export()); } }); @@ -585,6 +589,9 @@ public function abort(?\Throwable $reason = null) { $this->set_status(self::STATUS_ABORTED); $this->release_lock(); + // If configured to send email, attempt to notify of the abort reason. + $this->notify_on_abort($reason); + // TODO: We may want to make this the responsibility of the caller. if (isset($reason)) { throw $reason; @@ -845,4 +852,42 @@ private function setup_logging() { $this->logger = $log; } + + /** + * Send a notification email for abort if required. + * + * @param \Throwable $reason A throwable representing the reason for abort. + */ + public function notify_on_abort(?\Throwable $reason) { + // If configured to send email, attempt to notify of the abort reason. + $notifyemail = $this->dataflow->get('notifyonabort'); + if (empty($notifyemail)) { + return; + } + + $this->log('Sending abort notification email.', [], Logger::NOTICE); + $context = [ + 'flowname' => $this->dataflow->get('name'), + 'run' => $this->run->get('id'), + 'reason' => isset($reason) ? $reason->getMessage() : '' + ]; + $message = get_string('notifyonabort_message', 'tool_dataflows', $context); + + // First try to match the email with a Moodle user. + $to = \core_user::get_user_by_email($notifyemail); + + // Otherwise send it with a dummy account. + if (!$to) { + $to = \core_user::get_noreply_user(); + $to->email = $notifyemail; + $to->firstname = $this->dataflow->get('name'); + $to->emailstop = 0; + $to->maildisplay = true; + $to->mailformat = 1; + } + $from = \core_user::get_noreply_user(); + $subject = get_string('notifyonabort_subject', 'tool_dataflows', $this->dataflow->get('name')); + + email_to_user($to, $from, $subject, $message); + } } diff --git a/db/install.xml b/db/install.xml index b072c1b7..a71a972e 100644 --- a/db/install.xml +++ b/db/install.xml @@ -18,6 +18,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index 1bd380cb..83af26f0 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -304,6 +304,21 @@ function xmldb_tool_dataflows_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2023122201, 'tool', 'dataflows'); } + if ($oldversion < 2024030200) { + + // Define field loghandlers to be added to tool_dataflows. + $table = new xmldb_table('tool_dataflows'); + $field = new xmldb_field('notifyonabort', XMLDB_TYPE_CHAR, '255', null, null, null, '', 'confighash'); + + // Conditionally launch add field loghandlers. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Dataflows savepoint reached. + upgrade_plugin_savepoint(true, 2024030200, 'tool', 'dataflows'); + } + return true; } diff --git a/lang/en/tool_dataflows.php b/lang/en/tool_dataflows.php index ba22bc0f..e14a2466 100644 --- a/lang/en/tool_dataflows.php +++ b/lang/en/tool_dataflows.php @@ -101,6 +101,14 @@ Example:{$a->example} Note: If you use \'[dataroot]\', make sure to quote the value or it will be interpreted as an array.'; $string['error:vars_not_object'] = 'Vars must form a YAML object (Define each var as <var>: <value>)'; $string['error:invalid_yaml'] = 'Invalid YAML (Try quoting your value(s)): {$a}'; +$string['notifyonabort'] = 'Notify on abort'; +$string['notifyonabort_desc'] = 'Enter an email address to be notified on for dataflow aborts.'; +$string['notifyonabort_message'] = 'The dataflow {$a->flowname} was aborted on run {$a->run}. + +Reason: {$a->reason} + +See the run logs for further details.'; +$string['notifyonabort_subject'] = 'Dataflow run aborted: {$a}'; // Dataflow import form. $string['dataflow_file'] = 'Dataflow file'; diff --git a/version.php b/version.php index 6d03ee49..d6dd6f0d 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2023122201; -$plugin->release = 2023122201; +$plugin->version = 2024030200; +$plugin->release = 2024030200; $plugin->requires = 2022112800; // Our lowest supported Moodle (3.3.0). $plugin->supported = [400, 402]; // TODO $plugin->incompatible = ; // Available as of Moodle 3.9.0 or later.