Skip to content

Commit

Permalink
Fixed #369 by using instance method Messaging.SingleEmailMessage.setT…
Browse files Browse the repository at this point in the history
…argetObjectId() when sending failure emails to internal users
  • Loading branch information
jongpie committed Oct 13, 2023
1 parent f2fe85c commit 6adcffc
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public without sharing class LoggerEmailSender {
private static final List<Messaging.SingleEmailMessage> SENT_EMAILS = new List<Messaging.SingleEmailMessage>();

@TestVisible
private static final List<String> CACHED_APEX_ERROR_RECIPIENTS {
private static final List<ApexEmailNotification> CACHED_APEX_ERROR_RECIPIENTS {
get {
if (CACHED_APEX_ERROR_RECIPIENTS == null) {
CACHED_APEX_ERROR_RECIPIENTS = queryApexErrrorRecipients();
Expand Down Expand Up @@ -82,18 +82,32 @@ public without sharing class LoggerEmailSender {
return;
}

if (CACHED_APEX_ERROR_RECIPIENTS.isEmpty()) {
if (LoggerParameter.ENABLE_SYSTEM_MESSAGES) {
Logger.info('Logger - no Apex email recipients configured, skipping sending email');
if (CACHED_APEX_ERROR_RECIPIENTS.isEmpty() == true) {
if (LoggerParameter.ENABLE_SYSTEM_MESSAGES == true) {
// One of few limited places in the codebase (except tests) that should use System.debug()
// The rest of the codebase should use a method in Logger.cls
System.debug(System.LoggingLevel.WARN, 'Nebula Logger - no Apex email recipients configured, skipping sending email');
}
return;
}

Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.setToAddresses(CACHED_APEX_ERROR_RECIPIENTS);
message.setSubject(buildSubject(errorMessages));
message.setHtmlBody(buildHtmlBody(sobjectType, errorMessages));
sendEmail(message);
for (Schema.ApexEmailNotification notification : CACHED_APEX_ERROR_RECIPIENTS) {
Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.setSubject(buildSubject(errorMessages));
message.setHtmlBody(buildHtmlBody(sobjectType, errorMessages));

if (notification.UserId != null) {
message.setTargetObjectId(notification.UserId);
message.setSaveAsActivity(false);
} else if (String.isNotBlank(notification.Email) == true) {
message.setToAddresses(notification.Email.split(';'));
}

sendEmail(message);
}
// One of few limited places in the codebase (except tests) that should use System.debug()
// The rest of the codebase should use a method in Logger.cls
System.debug('>>> SENT_EMAILS: ' + SENT_EMAILS);
}

private static List<String> getErrorMessages(List<Database.SaveResult> saveResults) {
Expand Down Expand Up @@ -128,16 +142,20 @@ public without sharing class LoggerEmailSender {

if (LoggerParameter.ENABLE_SYSTEM_MESSAGES == false) {
return;
} else if (emailResults.get(0).success) {
Logger.info('Logger - The email was sent successfully');
} else if (emailResults.get(0).success == true) {
// One of few limited places in the codebase (except tests) that should use System.debug()
// The rest of the codebase should use a method in Logger.cls
System.debug(System.LoggingLevel.INFO, 'Nebula Logger - The email was sent successfully');
} else {
Logger.warn('Logger - The email failed to send: ' + emailResults.get(0).errors.get(0).message);
// One of few limited places in the codebase (except tests) that should use System.debug()
// The rest of the codebase should use a method in Logger.cls
System.debug(System.LoggingLevel.WARN, 'Nebula Logger - The email failed to send: ' + emailResults.get(0).errors.get(0).message);
}
}
}

private static String buildSubject(List<String> errorMessages) {
String emailSubjectTemplate = 'Logger - Error Notification - {0} ({1})';
String emailSubjectTemplate = 'Nebula Logger - Error Notification - {0} ({1})';
List<Object> emailSubjectInputs = new List<Object>{
LoggerEngineDataSelector.getInstance().getCachedOrganization().Name,
LoggerEngineDataSelector.getInstance().getCachedOrganization().Id
Expand All @@ -158,21 +176,14 @@ public without sharing class LoggerEmailSender {
return String.format(emailBodyTemplate, emailBodyInputs);
}

private static List<String> queryApexErrrorRecipients() {
private static List<ApexEmailNotification> queryApexErrrorRecipients() {
List<String> apexErrrorRecipients = new List<String>();
List<ApexEmailNotification> notifications = LogManagementDataSelector.getInstance().getCachedApexEmailNotifications();
if (System.Test.isRunningTest()) {
notifications.clear();
notifications.addAll(MOCK_NOTIFICATIONS);
}

for (ApexEmailNotification notification : notifications) {
if (notification.UserId != null) {
apexErrrorRecipients.add(notification.UserId);
} else if (String.isNotBlank(notification.Email)) {
apexErrrorRecipients.addAll(notification.Email.split(';'));
}
}
return apexErrrorRecipients;
return notifications;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,29 @@ private class LoggerEmailSender_Tests {
ApexEmailNotification invalidNotification = new ApexEmailNotification(Email = null, UserId = null);
LoggerEmailSender.MOCK_NOTIFICATIONS.addAll(new List<ApexEmailNotification>{ emailListNotification, userNotification, invalidNotification });

List<String> returnedRecipients = LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS;
List<ApexEmailNotification> returnedRecipients = LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS;

System.Assert.areEqual(3, returnedRecipients.size(), 'Should have returned 3 recipients: 1 for the user ID, and 2 for the email addresses');
for (String recipient : returnedRecipients) {
Boolean matchesUserNotification = String.valueOf(userNotification.UserId) == recipient;
Boolean matchesEmailListNotification = new Set<String>(emailListNotification.Email.split(';')).contains(recipient.trim());
System.Assert.areEqual(
true,
matchesUserNotification || matchesEmailListNotification,
'Returned recipient ' +
recipient +
' should match either the user notification or the email list notification\n' +
JSON.serializePretty(LoggerEmailSender.MOCK_NOTIFICATIONS)
);
}
// for (String recipient : returnedRecipients) {
// Boolean matchesUserNotification = String.valueOf(userNotification.UserId) == recipient;
// Boolean matchesEmailListNotification = new Set<String>(emailListNotification.Email.split(';')).contains(recipient.trim());
// System.Assert.areEqual(
// true,
// matchesUserNotification || matchesEmailListNotification,
// 'Returned recipient ' +
// recipient +
// ' should match either the user notification or the email list notification\n' +
// JSON.serializePretty(LoggerEmailSender.MOCK_NOTIFICATIONS)
// );
// }
}

@IsTest
static void it_should_send_email_notification_for_saveResult_errors_when_enabled() {
LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS.add(System.UserInfo.getUserId());
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(Email = '[email protected]'));
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(UserId = System.UserInfo.getUserId()));
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'No emails should have been sent yet');

// LogEntry__c requires a Log__c parent record, so inserting a LogEntry__c with no fields set will (intentionally) fail
List<Database.SaveResult> saveResultsWithErrors = new List<Database.SaveResult>{ LoggerMockDataCreator.createDatabaseSaveResult(false) };
LoggerEmailSender.sendErrorEmail(Schema.LogEntry__c.SObjectType, saveResultsWithErrors);

Expand All @@ -83,8 +83,8 @@ private class LoggerEmailSender_Tests {
LoggerEmailSender.SENT_EMAILS.get(0).getHtmlBody().contains(saveResultsWithErrors.get(0).errors.get(0).getMessage()),
'Email message should contain SaveResult error message'
);
if (LoggerEmailSender.IS_EMAIL_DELIVERABILITY_AVAILABLE) {
System.Assert.areEqual(1, System.Limits.getEmailInvocations(), 'Email should have been sent');
if (LoggerEmailSender.IS_EMAIL_DELIVERABILITY_AVAILABLE == true) {
System.Assert.areEqual(2, System.Limits.getEmailInvocations(), 'Emails should have been sent');
} else {
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'Deliverability is not currently enabled');
}
Expand All @@ -97,7 +97,6 @@ private class LoggerEmailSender_Tests {
LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS.clear();
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'No emails should have been sent yet');

// LogEntry__c requires a Log__c parent record, so inserting a LogEntry__c with no fields set will (intentionally) fail
List<Database.SaveResult> saveResultsWithErrors = new List<Database.SaveResult>{ LoggerMockDataCreator.createDatabaseSaveResult(false) };
LoggerEmailSender.sendErrorEmail(Schema.LogEntry__c.SObjectType, saveResultsWithErrors);

Expand All @@ -109,10 +108,10 @@ private class LoggerEmailSender_Tests {
static void it_should_not_send_email_notification_for_saveResult_errors_when_disabled() {
LoggerTestConfigurator.setMock(new LoggerParameter__mdt(DeveloperName = 'SendErrorEmailNotifications', Value__c = 'false'));
System.Assert.isFalse(LoggerParameter.SEND_ERROR_EMAIL_NOTIFICATIONS);
LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS.add(System.UserInfo.getUserId());
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(Email = '[email protected]'));
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(UserId = System.UserInfo.getUserId()));
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'No emails should have been sent yet');

// LogEntry__c requires a Log__c parent record, so inserting a LogEntry__c with no fields set will (intentionally) fail
List<Database.SaveResult> saveResultsWithErrors = new List<Database.SaveResult>{ LoggerMockDataCreator.createDatabaseSaveResult(false) };
LoggerEmailSender.sendErrorEmail(Schema.LogEntry__c.SObjectType, saveResultsWithErrors);

Expand All @@ -122,10 +121,10 @@ private class LoggerEmailSender_Tests {

@IsTest
static void it_should_send_email_notification_for_upsertResult_errors_when_enabled() {
LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS.add(System.UserInfo.getUserId());
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(Email = '[email protected]'));
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(UserId = System.UserInfo.getUserId()));
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'No emails should have been sent yet');

// LogEntry__c requires a Log__c parent record, so inserting a LogEntry__c with no fields set will (intentionally) fail
List<Database.UpsertResult> upsertResultsWithErrors = Database.upsert(new List<LogEntry__c>{ new LogEntry__c() }, false);
LoggerEmailSender.sendErrorEmail(Schema.LogEntry__c.SObjectType, upsertResultsWithErrors);

Expand All @@ -134,8 +133,8 @@ private class LoggerEmailSender_Tests {
LoggerEmailSender.SENT_EMAILS.get(0).getHtmlBody().contains(upsertResultsWithErrors.get(0).errors.get(0).getMessage()),
'Email message should contain UpsertResult error message'
);
if (LoggerEmailSender.IS_EMAIL_DELIVERABILITY_AVAILABLE) {
System.Assert.areEqual(1, System.Limits.getEmailInvocations(), 'Email should have been sent');
if (LoggerEmailSender.IS_EMAIL_DELIVERABILITY_AVAILABLE == true) {
System.Assert.areEqual(2, System.Limits.getEmailInvocations(), 'Emails should have been sent');
} else {
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'Deliverability is not currently enabled');
}
Expand All @@ -148,7 +147,6 @@ private class LoggerEmailSender_Tests {
LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS.clear();
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'No emails should have been sent yet');

// LogEntry__c requires a Log__c parent record, so inserting a LogEntry__c with no fields set will (intentionally) fail
List<Database.UpsertResult> upsertResultsWithErrors = Database.upsert(new List<LogEntry__c>{ new LogEntry__c() }, false);
LoggerEmailSender.sendErrorEmail(Schema.LogEntry__c.SObjectType, upsertResultsWithErrors);

Expand All @@ -160,10 +158,10 @@ private class LoggerEmailSender_Tests {
static void it_should_not_send_email_notification_for_upsertResult_errors_when_disabled() {
LoggerTestConfigurator.setMock(new LoggerParameter__mdt(DeveloperName = 'SendErrorEmailNotifications', Value__c = 'false'));
System.Assert.isFalse(LoggerParameter.SEND_ERROR_EMAIL_NOTIFICATIONS);
LoggerEmailSender.CACHED_APEX_ERROR_RECIPIENTS.add(System.UserInfo.getUserId());
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(Email = '[email protected]'));
LoggerEmailSender.MOCK_NOTIFICATIONS.add(new ApexEmailNotification(UserId = System.UserInfo.getUserId()));
System.Assert.areEqual(0, System.Limits.getEmailInvocations(), 'No emails should have been sent yet');

// LogEntry__c requires a Log__c parent record, so inserting a LogEntry__c with no fields set will (intentionally) fail
List<Database.UpsertResult> upsertResultsWithErrors = Database.upsert(new List<LogEntry__c>{ new LogEntry__c() }, false);
LoggerEmailSender.sendErrorEmail(Schema.LogEntry__c.SObjectType, upsertResultsWithErrors);

Expand Down

0 comments on commit 6adcffc

Please sign in to comment.