Skip to content
Snippets Groups Projects
Commit 7d2f1492 authored by Kunal Sachdev's avatar Kunal Sachdev Committed by Adam G-H
Browse files

Issue #3299612 by kunal.sachdev, yash.rode, phenaproxima: Send an email when...

Issue #3299612 by kunal.sachdev, yash.rode, phenaproxima: Send an email when an unattended update fails
parent f9893dc0
No related branches found
No related tags found
No related merge requests found
......@@ -42,14 +42,29 @@ function automatic_updates_mail(string $key, array &$message, array $params): vo
$options = [
'langcode' => $message['langcode'],
];
switch ($key) {
case 'cron_successful':
$message['subject'] = t("Drupal core was successfully updated", [], $options);
$message['body'][] = t('Congratulations!', [], $options);
$message['body'][] = t('Drupal core was automatically updated from @previous_version to @updated_version.', [
'@previous_version' => $params['previous_version'],
'@updated_version' => $params['updated_version'],
], $options);
break;
if ($key === 'cron_successful') {
$message['subject'] = t("Drupal core was successfully updated", [], $options);
$message['body'][] = t('Congratulations!', [], $options);
$message['body'][] = t('Drupal core was automatically updated from @previous_version to @updated_version.', [
'@previous_version' => $params['previous_version'],
'@updated_version' => $params['updated_version'],
], $options);
case 'cron_failed':
$message['subject'] = t("Drupal core update failed", [], $options);
$message['body'][] = t('Drupal core failed to update automatically from @previous_version to @target_version. The following error was logged:', [
'@previous_version' => $params['previous_version'],
'@target_version' => $params['target_version'],
], $options);
$message['body'][] = $params['error_message'];
break;
}
// If this email was related to an unattended update, explicitly state that
// this isn't supported yet.
if (str_starts_with($key, 'cron_')) {
$message['body'][] = t('This e-mail was sent by the Automatic Updates module. Unattended updates are not yet fully supported.', [], $options);
$message['body'][] = t('If you are using this feature in production, it is strongly recommended for you to visit your site and ensure that everything still looks good.', [], $options);
}
......
......@@ -182,6 +182,15 @@ class CronUpdater extends Updater {
$this->apply();
}
catch (\Throwable $e) {
// Send notifications about the failed update.
$mail_params = [
'previous_version' => $installed_version,
'target_version' => $target_version,
'error_message' => $e->getMessage(),
];
foreach ($this->getEmailRecipients() as $email => $langcode) {
$this->mailManager->mail('automatic_updates', 'cron_failed', $email, $langcode, $mail_params);
}
$this->logger->error($e->getMessage());
// If an error occurred during the pre-create event, the stage will be
......@@ -276,10 +285,8 @@ class CronUpdater extends Updater {
'previous_version' => $installed_version,
'updated_version' => $target_version,
];
$recipients = $this->configFactory->get('update.settings')
->get('notification.emails');
foreach ($recipients as $recipient) {
$this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $this->getEmailLangcode($recipient), $mail_params);
foreach ($this->getEmailRecipients() as $recipient => $langcode) {
$this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $langcode, $mail_params);
}
}
catch (\Throwable $e) {
......@@ -317,6 +324,23 @@ class CronUpdater extends Updater {
return $this->languageManager->getDefaultLanguage()->getId();
}
/**
* Returns an array of people to email with success or failure notifications.
*
* @return string[]
* An array whose keys are the email addresses to send notifications to, and
* values are the langcodes that they should be emailed in.
*/
protected function getEmailRecipients(): array {
$recipients = $this->configFactory->get('update.settings')
->get('notification.emails');
$emails = [];
foreach ($recipients as $recipient) {
$emails[$recipient] = $this->getEmailLangcode($recipient);
}
return $emails;
}
/**
* Gets the cron update mode.
*
......
......@@ -20,11 +20,9 @@ function automatic_updates_test_mail_alter(array &$message): void {
$message['subject'],
]);
foreach ($lines as $line) {
if (!($line instanceof TranslatableMarkup)) {
// All lines need to be translated.
return;
if ($line instanceof TranslatableMarkup) {
$line_langcodes[] = $line->getOption('langcode');
}
$line_langcodes[] = $line->getOption('langcode');
}
$message['line_langcodes'] = array_unique($line_langcodes);
}
......
......@@ -52,6 +52,16 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
*/
private $logger;
/**
* The people who should be emailed about successful or failed updates.
*
* The keys are the email addresses, and the values are the langcode they
* should be emailed in.
*
* @var string[]
*/
private $emailRecipients = [];
/**
* {@inheritdoc}
*/
......@@ -64,6 +74,26 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
->addLogger($this->logger);
$this->installEntitySchema('user');
$this->installSchema('user', ['users_data']);
// Prepare the recipient list to email when an update succeeds or fails.
// First, create a user whose preferred language is different from the
// default language, so we can be sure they're emailed in their preferred
// language; we also ensure that an email which doesn't correspond to a user
// account is emailed in the default language.
$default_language = $this->container->get('language_manager')
->getDefaultLanguage()
->getId();
$this->assertNotSame('fr', $default_language);
$account = $this->createUser([], NULL, FALSE, [
'preferred_langcode' => 'fr',
]);
$this->emailRecipients['emissary@deep.space'] = $default_language;
$this->emailRecipients[$account->getEmail()] = $account->getPreferredLangcode();
$this->config('update.settings')
->set('notification.emails', array_keys($this->emailRecipients))
->save();
}
/**
......@@ -349,51 +379,99 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
}
/**
* Tests that user 1 is emailed when an unattended update succeeds.
* Tests that email is sent when an unattended update succeeds.
*/
public function testEmailOnSuccess(): void {
$default_language = $this->container->get('language_manager')
->getDefaultLanguage()
->getId();
$this->assertNotSame('fr', $default_language);
$this->container->get('cron')->run();
$account = $this->createUser([], NULL, FALSE, [
'preferred_langcode' => 'fr',
// Ensure we sent a success message to all recipients.
$sent_messages = $this->getMails([
'subject' => "Drupal core was successfully updated",
]);
$this->assertNotEmpty($sent_messages);
$this->assertSame(count($this->emailRecipients), count($sent_messages));
$recipients = [
'emissary@deep.space',
$account->getEmail(),
];
$languages = [
$default_language,
$account->getPreferredLangcode(),
foreach ($sent_messages as $message) {
$email = $message['to'];
$this->assertSame($message['langcode'], $this->emailRecipients[$email]);
$this->assertCorrectMessageSent($email, $message, $message['langcode'], "Congratulations!\n\nDrupal core was automatically updated from 9.8.0 to 9.8.1.\n");
}
}
/**
* Data provider for ::testEmailOnFailure().
*
* @return string[][]
* The test cases.
*/
public function providerEmailOnFailure(): array {
return [
'pre-create' => [
PreCreateEvent::class,
],
'pre-require' => [
PreRequireEvent::class,
],
'pre-apply' => [
PreApplyEvent::class,
],
];
}
$this->config('update.settings')
->set('notification.emails', $recipients)
->save();
/**
* Tests that email is sent when an unattended update fails.
*
* @param string $event_class
* The event class that should trigger the failure.
*
* @dataProvider providerEmailOnFailure
*/
public function testEmailOnFailure(string $event_class): void {
$results = [
ValidationResult::createError(['Error while updating!']),
];
TestSubscriber1::setTestResult($results, $event_class);
$exception = new StageValidationException($results);
$this->container->get('cron')->run();
// Ensure we sent a success message to all recipients.
// Ensure we sent a failure message to all recipients.
$sent_messages = $this->getMails([
'subject' => "Drupal core was successfully updated",
'subject' => "Drupal core update failed",
]);
$this->assertSame(count($recipients), count($sent_messages));
$this->assertNotEmpty($sent_messages);
$this->assertSame(count($this->emailRecipients), count($sent_messages));
foreach ($sent_messages as $message) {
$email = $message['to'];
$this->assertSame($message['langcode'], $this->emailRecipients[$email]);
$this->assertCorrectMessageSent($email, $message, $message['langcode'], "Drupal core failed to update automatically from 9.8.0 to 9.8.1. The following\nerror was logged:\n\n" . $exception->getMessage());
}
}
/**
* Asserts correct message sent to correct recipient.
*
* @param string $expected_recipient
* The email address that should have received the message.
* @param array $sent_message
* The sent message, as processed by hook_mail().
* @param string $expected_language_code
* The language code that the recipient should have been emailed in.
* @param string $expected_body_text
* The expected message that the email body should contain.
*/
private function assertCorrectMessageSent(string $expected_recipient, array $sent_message, string $expected_language_code, string $expected_body_text): void {
// Ensure the messages had the correct body text, and were sent to the right
// people.
foreach ($sent_messages as $i => $message) {
$this->assertSame($message['to'], $recipients[$i]);
$this->assertStringStartsWith("Congratulations!\n\nDrupal core was automatically updated from 9.8.0 to 9.8.1.\n", $message['body']);
// The message, and every line in it, should have been sent in the
// expected language.
$langcode = $message['langcode'];
$this->assertSame($langcode, $languages[$i]);
// @see automatic_updates_test_mail_alter()
$this->assertArrayHasKey('line_langcodes', $message);
$this->assertSame([$langcode], $message['line_langcodes']);
}
$this->assertSame($sent_message['to'], $expected_recipient);
$this->assertStringStartsWith($expected_body_text, $sent_message['body']);
// The message, and every line in it, should have been sent in the
// expected language.
$this->assertSame($expected_language_code, $sent_message['langcode']);
// @see automatic_updates_test_mail_alter()
$this->assertArrayHasKey('line_langcodes', $sent_message);
$this->assertSame([$expected_language_code], $sent_message['line_langcodes']);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment