Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API response to after.send event, instead of returning to caller #294

Merged
merged 2 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions web/modules/custom/nys_sendgrid/src/Event/AfterSendEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,42 @@
namespace Drupal\nys_sendgrid\Event;

use Drupal\Component\EventDispatcher\Event;
use SendGrid\Response;

/**
* Defines the nys_sendgrid.after.send event.
*
* This event will be dispatched when sending mail, after mail() has finished.
* The API response referenced at $message['params']['sendgrid_mail']->response.
*/
class AfterSendEvent extends Event {

/**
* The message about to be sent.
* The Drupal array of the message which was sent.
*
* @var array
*/
protected array $message;

/**
* The Sendgrid API Response object.
*
* @var \SendGrid\Response|null
*/
protected ?Response $response = NULL;

/**
* Constructor.
*
* @param array $message
* A Drupal message array.
* @param \SendGrid\Response|null $response
* The Response object from the Sendgrid API call. Under certain failure
* conditions, this may be NULL.
*
* @see \Drupal\Core\Mail\MailManager::doMail()
*/
public function __construct(array $message) {
public function __construct(array $message, ?Response $response = NULL) {
$this->response = $response;
$this->message = $message;
}

Expand All @@ -38,4 +49,11 @@ public function getMessage(): array {
return $this->message;
}

/**
* Gets the API Response object.
*/
public function getResponse(): ?Response {
return $this->response;
}

}
148 changes: 69 additions & 79 deletions web/modules/custom/nys_sendgrid/src/Plugin/Mail/Sendgrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ public function __construct(\SendGrid $sendgrid, ModuleHandler $moduleHandler, C
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('nys_sendgrid_client'),
$container->get('module_handler'),
$container->get('config.factory'),
$container->get('event_dispatcher')
);
$container->get('nys_sendgrid_client'),
$container->get('module_handler'),
$container->get('config.factory'),
$container->get('event_dispatcher')
);
}

/**
Expand Down Expand Up @@ -185,14 +185,12 @@ public static function create(ContainerInterface $container, array $configuratio
* @see \Drupal\Core\Mail\MailManager::doMail()
*/
public function format(array $message): array {

// Save the local reference.
$this->message = $message;

// Every processing step can throw, but should handle catch locally.
// If any exceptions bubble up, cancel sending.
try {

// Ensure a Mail object is available.
$this->mailObj = $this->getMailObject($this->message['params']['sendgrid_mail'] ?? NULL);

Expand All @@ -207,7 +205,6 @@ public function format(array $message): array {
->injectCategories()
->injectHeaders()
->applyTemplate();

}
catch (\Throwable $e) {
$this->failSending('Sendgrid Mail object failed to validate.', ['message' => $e->getMessage()]);
Expand All @@ -226,7 +223,7 @@ public function format(array $message): array {
/**
* Provides a valid Sendgrid\Mail object.
*/
protected function getMailObject(Mail $object = NULL): Mail {
protected function getMailObject(?Mail $object = NULL): Mail {
return $object instanceof Mail ? $object : new Mail();
}

Expand All @@ -246,7 +243,7 @@ protected function applyTemplate(): self {
// Check if template assignment is suppressed. Suppression could be in
// the message params (preferred), or the global setting.
$suppressed = ($this->message['params']['suppress_template'] ?? FALSE)
|| ($this->localConfig->get('suppress_template') ?? FALSE);
|| ($this->localConfig->get('suppress_template') ?? FALSE);

// Templates pre-assigned by the caller take precedence. If no template,
// and not suppressed, discover and assign a template.
Expand Down Expand Up @@ -282,11 +279,11 @@ protected function applyTemplate(): self {
}
catch (\Throwable $e) {
$this->failSending(
'Email failed while assigning template (id=%id, actual=%actual)', [
'message' => $e->getMessage(),
'%actual' => $actual,
]
);
'Email failed while assigning template (id=%id, actual=%actual)', [
'message' => $e->getMessage(),
'%actual' => $actual,
]
);
}
}
}
Expand Down Expand Up @@ -353,37 +350,37 @@ protected function injectCategories(): self {
if ($this->message['module'] ?? '') {
// Check for suppression, which can be in config or message params.
$suppressed = ($this->message['params']['suppress_categories'] ?? FALSE)
|| $this->localConfig->get('suppress_categories');
|| $this->localConfig->get('suppress_categories');
if (!$suppressed) {
// Get the list of current categories.
$categories = array_map(
function ($v) {
return $v->getCategory();
},
$this->mailObj->getCategories() ?? []
);
function ($v) {
return $v->getCategory();
},
$this->mailObj->getCategories() ?? []
);

// Build which keys will be added.
$add = array_filter(
[
$this->message['module'] ?? '',
$this->message['key'] ?? '',
$this->message['id'] ?? '',
]
);
[
$this->message['module'] ?? '',
$this->message['key'] ?? '',
$this->message['id'] ?? '',
]
);

// Categories must be unique; ensure no duplicates are added.
try {
$this->mailObj->addCategories(array_diff($add, $categories));
}
catch (\Throwable $e) {
$this->failSending(
'Email failed while validating categories (id=%id)', [
'message' => $e->getMessage(),
'add' => $add,
'categories' => $categories,
]
);
'Email failed while validating categories (id=%id)', [
'message' => $e->getMessage(),
'add' => $add,
'categories' => $categories,
]
);
}
}
}
Expand All @@ -402,20 +399,20 @@ protected function validateBody(): self {
// If the preferred body is not populated, fallback to the alternate
// style from D7 legacy.
$body = ($this->message['body'] ?? [])
?: ($this->message['params']['body'] ?? []);
?: ($this->message['params']['body'] ?? []);

// Typecast each body part to string so addContent() doesn't cry.
if (!is_array($body)) {
$body = [$body];
}
$body = implode(
"\n\n",
array_map(
function ($i) {
return (string) $i;
}, $body
)
);
"\n\n",
array_map(
function ($i) {
return (string) $i;
}, $body
)
);

$content_type = $this->message['params']['Content-Type'] ?? '';
if (!$content_type) {
Expand All @@ -437,7 +434,7 @@ function ($i) {
'%body' => $body,
'%ct' => $content_type,
]
);
);
}
}

Expand All @@ -458,11 +455,11 @@ protected function validateSubject(): self {
}
catch (\Throwable $e) {
$this->failSending(
'Email failed while validating subject (id=%id, subj=%subj)', [
'message' => $e->getMessage(),
'%subj' => $subject,
]
);
'Email failed while validating subject (id=%id, subj=%subj)', [
'message' => $e->getMessage(),
'%subj' => $subject,
]
);
}
}

Expand Down Expand Up @@ -508,19 +505,19 @@ protected function setRerouting(): self {
elseif (!$recipient->getName()) {
$original_email = $this->message['headers']['X-Rerouted-Original-to'] ?? '';
$new_name = 'reroute_email ' .
($original_email ? 'from ' . $original_email : '<unknown>');
($original_email ? 'from ' . $original_email : '<unknown>');
$recipient->setName($new_name);
}
}
}
}
catch (\Throwable $e) {
$this->failSending(
'Email failed during rerouting attempt. (id=%id, dest=%dest)', [
'message' => $e->getMessage(),
'%dest' => $dest,
]
);
'Email failed during rerouting attempt. (id=%id, dest=%dest)', [
'message' => $e->getMessage(),
'%dest' => $dest,
]
);
}
}
}
Expand All @@ -538,7 +535,6 @@ protected function setRerouting(): self {
* @return $this
*/
protected function validateRecipients(): self {

// Detect any existing recipients.
$persons = $this->mailObj->getPersonalizations() ?? [];
$found_to = FALSE;
Expand All @@ -557,11 +553,11 @@ protected function validateRecipients(): self {
}
catch (\Throwable $e) {
$this->failSending(
'Email failed due to poorly-formed "To" address (id=%id, addr=%addr)', [
'message' => $e->getMessage(),
'%addr' => $to_addr,
]
);
'Email failed due to poorly-formed "To" address (id=%id, addr=%addr)', [
'message' => $e->getMessage(),
'%addr' => $to_addr,
]
);
}
}

Expand All @@ -574,11 +570,10 @@ protected function validateRecipients(): self {
* @return $this
*/
protected function validateFrom(): self {

// If a "From" address is not found, make one.
if (!(($this->mailObj->getFrom() instanceof From)
&& $this->mailObj->getFrom()->getEmail())
) {
&& $this->mailObj->getFrom()->getEmail())
) {
// Parse the email address from the message.
$full_from = $this->message['params']['from'] ?? $this->message['from'];
$from_email = Helper::parseAddress($full_from);
Expand All @@ -589,11 +584,11 @@ protected function validateFrom(): self {
}
catch (\Throwable $e) {
$this->failSending(
'Email failed due to poorly-formed "From" address (id=%id, addr=%addr)', [
'message' => $e->getMessage(),
'%addr' => $from_email,
]
);
'Email failed due to poorly-formed "From" address (id=%id, addr=%addr)', [
'message' => $e->getMessage(),
'%addr' => $from_email,
]
);
}
}

Expand Down Expand Up @@ -627,20 +622,15 @@ public function mail(array $message): bool {
->error('Sending mail generated an exception.', ['%message' => $e->getMessage()]);
}

// Send the response back with the object.
$sgm->response = $response;

// Check for success. We consider a 200/202 response code successful.
$success = in_array($response_code, ['200', '202']);
// If successful, dispatch the after.send event.
if ($success) {
// @phpstan-ignore-next-line
$this->dispatcher->dispatch(new AfterSendEvent($message), Events::AFTER_SEND);
}
// If not successful, but not an exception, log the reason.
elseif ($response) {
$this->localLogger
->error('Sendgrid API rejected a mail attempt.', ['%response' => $response->body()]);

// Dispatch the after.send event.
$this->dispatcher->dispatch(new AfterSendEvent($message, $response), Events::AFTER_SEND);

// If not successful, log the reason.
if (!$success) {
$this->localLogger->error('Sendgrid API rejected a mail attempt.', ['%response' => $response?->body() ?? 'NULL response']);
}

// Return the success/failure.
Expand Down
Loading