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 ...@@ -42,14 +42,29 @@ function automatic_updates_mail(string $key, array &$message, array $params): vo
$options = [ $options = [
'langcode' => $message['langcode'], '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') { case 'cron_failed':
$message['subject'] = t("Drupal core was successfully updated", [], $options); $message['subject'] = t("Drupal core update failed", [], $options);
$message['body'][] = t('Congratulations!', [], $options); $message['body'][] = t('Drupal core failed to update automatically from @previous_version to @target_version. The following error was logged:', [
$message['body'][] = t('Drupal core was automatically updated from @previous_version to @updated_version.', [ '@previous_version' => $params['previous_version'],
'@previous_version' => $params['previous_version'], '@target_version' => $params['target_version'],
'@updated_version' => $params['updated_version'], ], $options);
], $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('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); $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 { ...@@ -182,6 +182,15 @@ class CronUpdater extends Updater {
$this->apply(); $this->apply();
} }
catch (\Throwable $e) { 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()); $this->logger->error($e->getMessage());
// If an error occurred during the pre-create event, the stage will be // If an error occurred during the pre-create event, the stage will be
...@@ -276,10 +285,8 @@ class CronUpdater extends Updater { ...@@ -276,10 +285,8 @@ class CronUpdater extends Updater {
'previous_version' => $installed_version, 'previous_version' => $installed_version,
'updated_version' => $target_version, 'updated_version' => $target_version,
]; ];
$recipients = $this->configFactory->get('update.settings') foreach ($this->getEmailRecipients() as $recipient => $langcode) {
->get('notification.emails'); $this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $langcode, $mail_params);
foreach ($recipients as $recipient) {
$this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $this->getEmailLangcode($recipient), $mail_params);
} }
} }
catch (\Throwable $e) { catch (\Throwable $e) {
...@@ -317,6 +324,23 @@ class CronUpdater extends Updater { ...@@ -317,6 +324,23 @@ class CronUpdater extends Updater {
return $this->languageManager->getDefaultLanguage()->getId(); 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. * Gets the cron update mode.
* *
......
...@@ -20,11 +20,9 @@ function automatic_updates_test_mail_alter(array &$message): void { ...@@ -20,11 +20,9 @@ function automatic_updates_test_mail_alter(array &$message): void {
$message['subject'], $message['subject'],
]); ]);
foreach ($lines as $line) { foreach ($lines as $line) {
if (!($line instanceof TranslatableMarkup)) { if ($line instanceof TranslatableMarkup) {
// All lines need to be translated. $line_langcodes[] = $line->getOption('langcode');
return;
} }
$line_langcodes[] = $line->getOption('langcode');
} }
$message['line_langcodes'] = array_unique($line_langcodes); $message['line_langcodes'] = array_unique($line_langcodes);
} }
......
...@@ -52,6 +52,16 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase { ...@@ -52,6 +52,16 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
*/ */
private $logger; 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} * {@inheritdoc}
*/ */
...@@ -64,6 +74,26 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase { ...@@ -64,6 +74,26 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
->addLogger($this->logger); ->addLogger($this->logger);
$this->installEntitySchema('user'); $this->installEntitySchema('user');
$this->installSchema('user', ['users_data']); $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 { ...@@ -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 { public function testEmailOnSuccess(): void {
$default_language = $this->container->get('language_manager') $this->container->get('cron')->run();
->getDefaultLanguage()
->getId();
$this->assertNotSame('fr', $default_language);
$account = $this->createUser([], NULL, FALSE, [ // Ensure we sent a success message to all recipients.
'preferred_langcode' => 'fr', $sent_messages = $this->getMails([
'subject' => "Drupal core was successfully updated",
]); ]);
$this->assertNotEmpty($sent_messages);
$this->assertSame(count($this->emailRecipients), count($sent_messages));
$recipients = [ foreach ($sent_messages as $message) {
'emissary@deep.space', $email = $message['to'];
$account->getEmail(), $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");
$languages = [ }
$default_language, }
$account->getPreferredLangcode(),
/**
* 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) * Tests that email is sent when an unattended update fails.
->save(); *
* @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(); $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([ $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 // Ensure the messages had the correct body text, and were sent to the right
// people. // people.
foreach ($sent_messages as $i => $message) { $this->assertSame($sent_message['to'], $expected_recipient);
$this->assertSame($message['to'], $recipients[$i]); $this->assertStringStartsWith($expected_body_text, $sent_message['body']);
$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
// The message, and every line in it, should have been sent in the // expected language.
// expected language. $this->assertSame($expected_language_code, $sent_message['langcode']);
$langcode = $message['langcode']; // @see automatic_updates_test_mail_alter()
$this->assertSame($langcode, $languages[$i]); $this->assertArrayHasKey('line_langcodes', $sent_message);
// @see automatic_updates_test_mail_alter() $this->assertSame([$expected_language_code], $sent_message['line_langcodes']);
$this->assertArrayHasKey('line_langcodes', $message);
$this->assertSame([$langcode], $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