Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class EmailHistoryDto : ExtensibleAuditedEntityDto<Guid>
public string Body { get; set; } = string.Empty;
public EmailHistoryUserDto? SentBy { get; set; }
public string TemplateName { get; set; } = string.Empty;
public DateTime? SendOnDateTime { get; set; }
}

public class EmailHistoryUserDto : EntityDto<Guid>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Unity.Notifications.EmailNotifications
{
/// <summary>
/// Groups common email content fields to reduce method parameter count (S107).
/// </summary>
public sealed record EmailMessageParams(
string EmailTo,
string Body,
string Subject,
string? EmailFrom,
string? EmailTemplateName,
string? EmailCC = null,
string? EmailBCC = null,
System.DateTime? SendOnDateTime = null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,42 +31,44 @@ public class EmailNotificationManager(
EmailAttachmentService emailAttachmentService,
ISettingProvider settingProvider) : DomainService, IEmailNotificationManager
{
public async Task<EmailLog?> CreateEmailLogAsync(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<EmailLog?> CreateEmailLogAsync(EmailMessageParams email, Guid applicationId)
{
return await CreateEmailLogAsync(emailTo, body, subject, applicationId, emailFrom, EmailStatus.Initialized, emailTemplateName, emailCC, emailBCC);
return await CreateEmailLogAsync(email, applicationId, EmailStatus.Initialized);
}

[RemoteService(false)]
public async Task<EmailLog?> CreateEmailLogAsync(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<EmailLog?> CreateEmailLogAsync(EmailMessageParams email, Guid applicationId, string? status)
{
if (string.IsNullOrEmpty(emailTo))
if (string.IsNullOrEmpty(email.EmailTo))
{
return null;
}
var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, "html", emailTemplateName, emailCC, emailBCC);
var emailObject = await GetEmailObjectAsync(email, "html");
EmailLog emailLog = new();
emailLog = UpdateMappedEmailLog(emailLog, emailObject);
emailLog.ApplicationId = applicationId;
emailLog.Status = status ?? EmailStatus.Initialized;
emailLog.SendOnDateTime = email.SendOnDateTime;

// When being called here the current tenant is in context - verified by looking at the tenant id
EmailLog loggedEmail = await emailLogsRepository.InsertAsync(emailLog, autoSave: true);
return loggedEmail;
}

public async Task<EmailLog?> UpdateEmailLogAsync(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<EmailLog?> UpdateEmailLogAsync(Guid emailId, EmailMessageParams email, Guid applicationId, string? status)
{
if (string.IsNullOrEmpty(emailTo))
if (string.IsNullOrEmpty(email.EmailTo))
{
return null;
}

var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, "html", emailTemplateName, emailCC, emailBCC);
var emailObject = await GetEmailObjectAsync(email, "html");
EmailLog emailLog = await emailLogsRepository.GetAsync(emailId);
emailLog = UpdateMappedEmailLog(emailLog, emailObject);
emailLog.ApplicationId = applicationId;
emailLog.Id = emailId;
emailLog.Status = status ?? EmailStatus.Initialized;
emailLog.SendOnDateTime = email.SendOnDateTime;

// When being called here the current tenant is in context - verified by looking at the tenant id
EmailLog loggedEmail = await emailLogsRepository.UpdateAsync(emailLog, autoSave: true);
Expand Down Expand Up @@ -131,11 +133,11 @@ public async Task DeleteEmailLogAsync(Guid id)
/// <param name="emailCC">CC email addresses</param>
/// <param name="emailBCC">BCC email addresses</param>
/// <returns>HttpResponseMessage indicating the result of the operation</returns>
public async Task<HttpResponseMessage> SendEmailAsync(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<HttpResponseMessage> SendEmailAsync(EmailMessageParams email, string? emailBodyType = null)
{
try
{
if (string.IsNullOrEmpty(emailTo))
if (string.IsNullOrEmpty(email.EmailTo))
{
Logger.LogError("EmailNotificationManager->SendEmailAsync: The 'emailTo' parameter is null or empty.");
return new HttpResponseMessage(HttpStatusCode.BadRequest)
Expand All @@ -145,8 +147,7 @@ public async Task<HttpResponseMessage> SendEmailAsync(string emailTo, string bod
}

// Send the email using the CHES client service
var emailObject = await GetEmailObjectAsync(
emailTo, body, subject, emailFrom, emailBodyType, emailTemplateName, emailCC, emailBCC, excludeTemplate: true);
var emailObject = await GetEmailObjectAsync(email, emailBodyType, excludeTemplate: true);

var response = await chesClientService.SendAsync(emailObject);

Expand All @@ -173,15 +174,6 @@ public async Task<HttpResponseMessage> SendEmailAsync(EmailLog emailLog)
{
try
{
if (emailLog == null)
{
Logger.LogError("EmailNotificationManager->SendEmailAsync: The 'emailLog' parameter is null.");
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent("'emailLog' cannot be null.")
};
}

if (string.IsNullOrEmpty(emailLog.ToAddress))
{
Logger.LogError("EmailNotificationManager->SendEmailAsync: The 'emailLog.ToAddress' parameter is null or empty.");
Expand All @@ -201,7 +193,7 @@ public async Task<HttpResponseMessage> SendEmailAsync(EmailLog emailLog)
}
catch (Exception ex)
{
Logger.LogError(ex, "EmailNotificationManager->SendEmailAsync: Exception occurred while sending email for EmailLog {EmailId}.", emailLog?.Id);
Logger.LogError(ex, "EmailNotificationManager->SendEmailAsync: Exception occurred while sending email for EmailLog {EmailId}.", emailLog.Id);
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent($"An exception occurred while sending the email: {ex.Message}")
Expand Down Expand Up @@ -237,22 +229,16 @@ public async Task<int> GetPendingEmailsCountAsync()
var allEmailLogs = await emailLogsRepository.GetListAsync();
var emailLogs = allEmailLogs.Where(filter.Compile()).ToList();

// Ensure we're returning 0 if no logs are found
return emailLogs?.Count ?? 0;
return emailLogs.Count;
}

public async Task<dynamic> BuildEmailObjectWithAttachmentsAsync(EmailLog emailLog)
{
// Get base email object (without attachments)
var emailObject = await GetEmailObjectAsync(
emailLog.ToAddress,
emailLog.Body,
emailLog.Subject,
emailLog.FromAddress,
new EmailMessageParams(emailLog.ToAddress, emailLog.Body, emailLog.Subject,
emailLog.FromAddress, emailLog.TemplateName, emailLog.CC, emailLog.BCC),
emailLog.BodyType,
emailLog.TemplateName,
emailLog.CC,
emailLog.BCC,
excludeTemplate: true);

// Retrieve attachments from S3
Expand Down Expand Up @@ -281,35 +267,35 @@ public async Task<dynamic> BuildEmailObjectWithAttachmentsAsync(EmailLog emailLo
emailObjectDictionary["attachments"] = attachmentList.ToArray();
}

if (emailLog.SendOnDateTime.HasValue)
{
var emailObjectDictionary = (IDictionary<string, object?>)emailObject;
emailObjectDictionary["delayTS"] = new DateTimeOffset(emailLog.SendOnDateTime.Value, TimeSpan.Zero).ToUnixTimeMilliseconds();
}

return emailObject;
}

protected virtual async Task<dynamic> GetEmailObjectAsync(
string emailTo,
string body,
string subject,
string? emailFrom,
string? emailBodyType,
string? emailTemplateName,
string? emailCC = null,
string? emailBCC = null,
bool excludeTemplate = false)
EmailMessageParams email,
string? emailBodyType = null,
bool excludeTemplate = false)
{
var toList = emailTo.ParseEmailList() ?? [];
var ccList = emailCC.ParseEmailList();
var bccList = emailBCC.ParseEmailList();
var toList = email.EmailTo.ParseEmailList() ?? [];
var ccList = email.EmailCC.ParseEmailList();
var bccList = email.EmailBCC.ParseEmailList();

var defaultFromAddress = await settingProvider.GetOrNullAsync(NotificationsSettings.Mailing.DefaultFromAddress);

dynamic emailObject = new ExpandoObject();
var emailObjectDictionary = (IDictionary<string, object?>)emailObject;

emailObjectDictionary["body"] = body;
emailObjectDictionary["body"] = email.Body;
emailObjectDictionary["bodyType"] = emailBodyType ?? "text";
emailObjectDictionary["encoding"] = "utf-8";
emailObjectDictionary["from"] = emailFrom ?? defaultFromAddress ?? "NoReply@gov.bc.ca";
emailObjectDictionary["from"] = email.EmailFrom ?? defaultFromAddress ?? "NoReply@gov.bc.ca";
emailObjectDictionary["priority"] = "normal";
emailObjectDictionary["subject"] = subject;
emailObjectDictionary["subject"] = email.Subject;
emailObjectDictionary["tag"] = "tag";
emailObjectDictionary["to"] = toList;

Expand All @@ -323,11 +309,17 @@ protected virtual async Task<dynamic> GetEmailObjectAsync(
emailObjectDictionary["bcc"] = bccList;
}

// delayTS: desired UTC send time as Unix milliseconds; 0 = send immediately.
if (email.SendOnDateTime.HasValue)
{
emailObjectDictionary["delayTS"] = new DateTimeOffset(email.SendOnDateTime.Value, TimeSpan.Zero).ToUnixTimeMilliseconds();
}

// templateName is not part of the CHES MessageObject schema
// store it on the EmailLog but don't send it to the API.
if (!excludeTemplate)
{
emailObjectDictionary["templateName"] = emailTemplateName;
emailObjectDictionary["templateName"] = email.EmailTemplateName;
}

return emailObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,20 @@ public async Task<int> GetEmailsChesWithNoResponseCountAsync()
return await emailNotificationManager.GetPendingEmailsCountAsync();
}

public async Task<EmailLog?> UpdateEmailLog(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<EmailLog?> UpdateEmailLog(Guid emailId, EmailMessageParams email, Guid applicationId, string? status)
{
return await emailNotificationManager.UpdateEmailLogAsync(emailId, emailTo, body, subject, applicationId, emailFrom, status, emailTemplateName, emailCC, emailBCC);
return await emailNotificationManager.UpdateEmailLogAsync(emailId, email, applicationId, status);
}

public async Task<EmailLog?> InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<EmailLog?> InitializeEmailLog(EmailMessageParams email, Guid applicationId)
{
return await emailNotificationManager.CreateEmailLogAsync(emailTo, body, subject, applicationId, emailFrom, emailTemplateName, emailCC, emailBCC);
return await emailNotificationManager.CreateEmailLogAsync(email, applicationId);
}

[RemoteService(false)]
public async Task<EmailLog?> InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<EmailLog?> InitializeEmailLog(EmailMessageParams email, Guid applicationId, string? status)
{
return await emailNotificationManager.CreateEmailLogAsync(emailTo, body, subject, applicationId, emailFrom, status, emailTemplateName, emailCC, emailBCC);
return await emailNotificationManager.CreateEmailLogAsync(email, applicationId, status);
}

protected virtual async Task NotifyTeamsChannel(string chesEmailError)
Expand Down Expand Up @@ -153,7 +153,8 @@ public async Task<HttpResponseMessage> SendCommentNotification(EmailCommentDto i
foreach (var email in input.MentionNamesEmail)
{
var toEmail = email;
res = await emailNotificationManager.SendEmailAsync(toEmail, htmlBody, subject, fromEmail, "html", input.EmailTemplateName);
res = await emailNotificationManager.SendEmailAsync(
new EmailMessageParams(toEmail, htmlBody, subject, fromEmail, input.EmailTemplateName), "html");
}
}
else
Expand Down Expand Up @@ -188,9 +189,9 @@ public async Task<HttpResponseMessage> SendCommentNotification(EmailCommentDto i
/// <param name="emailCC">CC email addresses</param>
/// <param name="emailBCC">BCC email addresses</param>
/// <returns>HttpResponseMessage indicating the result of the operation</returns>
public async Task<HttpResponseMessage> SendEmailNotification(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null)
public async Task<HttpResponseMessage> SendEmailNotification(EmailMessageParams email, string? emailBodyType = null)
{
return await emailNotificationManager.SendEmailAsync(emailTo, body, subject, emailFrom, emailBodyType, emailTemplateName, emailCC, emailBCC);
return await emailNotificationManager.SendEmailAsync(email, emailBodyType);
}

/// <summary>
Expand Down Expand Up @@ -257,6 +258,7 @@ public async Task UpdateSettings(NotificationsSettingsDto settingsDto)
{
await UpdateTenantSettings(NotificationsSettings.Mailing.DefaultFromAddress, settingsDto.DefaultFromAddress);
await UpdateTenantSettings(NotificationsSettings.Mailing.EmailMaxRetryAttempts, settingsDto.MaximumRetryAttempts);
await settingManager.SetForCurrentTenantAsync(NotificationsSettings.Mailing.EnableEmailDelay, settingsDto.EnableEmailDelay ? "true" : "false");
}

private async Task UpdateTenantSettings(string settingKey, string valueString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ public interface IEmailNotificationManager
/// <summary>
/// Creates and initializes a new email log
/// </summary>
Task<EmailLog?> CreateEmailLogAsync(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<EmailLog?> CreateEmailLogAsync(EmailMessageParams email, Guid applicationId);

/// <summary>
/// Creates and initializes a new email log with status
/// </summary>
Task<EmailLog?> CreateEmailLogAsync(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<EmailLog?> CreateEmailLogAsync(EmailMessageParams email, Guid applicationId, string? status);

/// <summary>
/// Updates an existing email log
/// </summary>
Task<EmailLog?> UpdateEmailLogAsync(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<EmailLog?> UpdateEmailLogAsync(Guid emailId, EmailMessageParams email, Guid applicationId, string? status);

/// <summary>
/// Retrieves an email log by ID
Expand All @@ -44,7 +44,7 @@ public interface IEmailNotificationManager
/// <summary>
/// Sends an email notification using CHES
/// </summary>
Task<HttpResponseMessage> SendEmailAsync(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<HttpResponseMessage> SendEmailAsync(EmailMessageParams email, string? emailBodyType = null);

/// <summary>
/// Sends an email notification from an EmailLog (with S3 attachments support)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ namespace Unity.Notifications.EmailNotifications
{
public interface IEmailNotificationService : IApplicationService
{
Task<EmailLog?> UpdateEmailLog(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<EmailLog?> InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<EmailLog?> InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<EmailLog?> UpdateEmailLog(Guid emailId, EmailMessageParams email, Guid applicationId, string? status);
Task<EmailLog?> InitializeEmailLog(EmailMessageParams email, Guid applicationId, string? status);
Task<EmailLog?> InitializeEmailLog(EmailMessageParams email, Guid applicationId);
Task<EmailLog?> GetEmailLogById(Guid id);
Task<HttpResponseMessage> SendCommentNotification(EmailCommentDto input);
Task<HttpResponseMessage> SendEmailNotification(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null);
Task<HttpResponseMessage> SendEmailNotification(EmailMessageParams email, string? emailBodyType = null);
Task<HttpResponseMessage> SendEmailNotification(EmailLog emailLog);
Task SendEmailToQueue(EmailLog emailLog);
Task<List<EmailHistoryDto>> GetHistoryByApplicationId(Guid applicationId);
Expand Down
Loading
Loading