Skip to content

Commit

Permalink
Do a small refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
mtokarev committed May 15, 2024
1 parent 8664f78 commit 8ca0c44
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 180 deletions.
16 changes: 12 additions & 4 deletions mailSender/Ex3.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Text.Json.Serialization;

namespace mailSender
Expand All @@ -15,8 +14,17 @@ public DateTime Dob
{
get
{
DateTime.TryParse(DobStr, out var value);
return value;
// We expect to have string in the specific format 'dd/MM/yyyy'
if (DateTime.TryParseExact(DobStr,
"dd/MM/yyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var value))
{
return value;
}

return DateTime.MinValue;
}
set
{
Expand Down
153 changes: 153 additions & 0 deletions mailSender/MailService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Serilog.Core;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Net.Mail;

namespace mailSender
{
public class MailService
{
private readonly IConfiguration _config;
private readonly Logger _logger;
private readonly EmailAddress _mailFrom;
private readonly bool _isSimulationModeEnabled;
private readonly string _mailSubject,
_ccRecipient;

public MailService(IConfiguration config, Logger logger)
{
ValidateAndAssignConfig(config);

_config = config;
_logger = logger;

_mailFrom = new EmailAddress(_config.GetSection("SendGrid:SenderEmail").Value,
_config.GetSection("SendGrid:SenderName").Value);
_isSimulationModeEnabled = bool.Parse(
_config.GetSection("SendGrid:SimulationModeEnabled").Value);
_mailSubject = _config.GetSection("SendGrid:MailSubject").Value;
_ccRecipient = _config.GetSection("SendGrid:CcRecipient").Value;

}

public async Task SendBrthEmailsAsync(List<UserPrincipalExtension> sendTos)
{
string mailTemplate;

try
{
// Read file from mail template.
mailTemplate = File.ReadAllText(_config.GetSection("SendGrid:MailTemplateHtml").Value);
}
catch (Exception e)
{
_logger.Error($"Unable to read mail template file: '{_config.GetSection("SendGrid:MailTemplateHtml").Value}'. Exception: '{e.Message}'.");

return;
}

if (_isSimulationModeEnabled)
{
_logger.Information("--- Simulation mode turned ON. No email will be sent to the users. You can turn it off in appsettings.json");
}

var client = new SendGridClient(_config.GetSection("SendGrid:ApiKey").Value);

foreach (var user in sendTos)
{
// Replacing message subject and body with user display name.
SendGridMessage mail = CreateMail(
_mailFrom,
_mailSubject,
mailTemplate,
user);

// Adding user email to the 'To' field.
mail.AddTo(user.EmailAddress);

// If we have recipient to add to CC.
if (!string.IsNullOrEmpty(_ccRecipient))
{
mail.AddCc(_ccRecipient);
}

// If simulation mode is enabled just log message and continue.
if (_isSimulationModeEnabled)
{
_logger.Information($"E-mail won't sent to '{user.EmailAddress}'.");
continue;
}

// Adding task to the list.
// Creating client and sending messages.
var result = await client.SendEmailAsync(mail);

// Logging results
if (result.StatusCode != HttpStatusCode.Accepted)
{
_logger.Error($"Unable send email to: '{user.EmailAddress}'. Status code '{result.StatusCode}'.");
}
else if (result.StatusCode == HttpStatusCode.Accepted)
{
_logger.Information($"Mail has been sent to: '{user.EmailAddress}'.");
}
}
}

private static SendGridMessage CreateMail(EmailAddress mailFrom,
string mailSubject,
string mailTemplate,
UserPrincipalExtension user)
{
var compiledMailTemplate = mailTemplate.Replace("{{name}}", user.DisplayName);
var compiledMailSubject = mailSubject.Replace("{{name}}", user.DisplayName);

var mail = new SendGridMessage
{
From = mailFrom,
Subject = compiledMailSubject,
PlainTextContent = compiledMailTemplate,
HtmlContent = compiledMailTemplate
};
return mail;
}

private void ValidateAndAssignConfig(IConfiguration config)
{
string sender = _config.GetSection("SendGrid:SenderEmail").Value;
if (string.IsNullOrEmpty(_config.GetSection("SendGrid:SenderName").Value)
&& string.IsNullOrEmpty(sender))
{
_logger.Fatal("Please provide 'SenderEmail' and 'SenderName' in appSettings.json");
}

if (!string.IsNullOrEmpty(sender) && !MailAddress.TryCreate(sender, out var _))
{
_logger.Fatal("'SenderEmail' must be a valid email address. Value provided: '{Sender}'.",
sender);
}

if (bool.TryParse(_config.GetSection("SendGrid:SimulationModeEnabled").Value, out bool _))
{
_logger.Fatal("'SimulationModeEnabled' must to be set in appsetting.json as 'true' or 'false'.");
}

string ccRecipient = _config.GetSection("SendGrid:CcRecipient").Value;
if (!MailAddress.TryCreate(sender, out var _)
&& !string.IsNullOrEmpty(ccRecipient))
{
_logger.Fatal("'CcResipient' must be empty or have a valid email address. Value provided: '{CcRecipient}'.",
ccRecipient);
}

throw new ArgumentException($"One or more parameter provided for '{nameof(MailService)}' are incorrect. " +
"Please check the log for more information");
}
}
}
171 changes: 10 additions & 161 deletions mailSender/Program.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
using Microsoft.Extensions.Configuration;
using SendGrid;
using SendGrid.Helpers.Mail;
using Serilog;
using Serilog.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;

namespace mailSender
Expand All @@ -18,88 +12,32 @@ class Program
{
private static IConfigurationRoot _config;
private static Logger _logger;
static void Main(string[] args)

static async Task Main(string[] args)
{
// Lets create stopwatch and monitor execution time
// Create stopwatch to monitor execution time
var stopWatch = new Stopwatch();
stopWatch.Start();

// Load config and init logs.
_config = LoadConfiguration("appsettings.json");
_logger = InitLogger(_config);

_logger.Information($"Starting '{nameof(mailSender)}'.");

// Getting all user enabled objects and filtering them to the list
// who celebrate birthday today.
var enabledUsers = GetEnabledDomainUsers(domainName: Environment.UserDomainName);
var celebratingUser = GetCelebratingUsers(enabledUsers);
// Find all active users who celebrate birthday today
var userService = new UserService(_logger);
var enabledUsers = userService.GetEnabledDomainUsers(domainName: Environment.UserDomainName);
var celebratingUser = userService.GetCelebratingUsers(enabledUsers);

// Send celebration message and waiting when task is done.
var task = GenerateEmailsAsync(celebratingUser);
task.Wait();
var mailService = new MailService(_config, _logger);
await mailService.SendBrthEmailsAsync(celebratingUser);

// Stop timer and log time
stopWatch.Stop();
_logger.Information($"Exectution '{nameof(mailSender)}' has been finished. Running time: '{stopWatch.Elapsed.TotalSeconds}s'.");
}
private static List<UserPrincipalExtension> GetEnabledDomainUsers(string domainName)
{
_logger.Information($"Run user scanning in domain: '{domainName}'.");

var myDomainUsers = new List<UserPrincipalExtension>();
using (var ctx = new PrincipalContext(ContextType.Domain, domainName))
{
var userPrinciple = new UserPrincipalExtension(ctx);
using var search = new PrincipalSearcher(userPrinciple);

// Filter only active users
userPrinciple.Enabled = true;
search.QueryFilter = userPrinciple;

foreach (var domainUser in search.FindAll())
{
if (domainUser.DisplayName != null)
{
myDomainUsers.Add((UserPrincipalExtension)domainUser);
}
}
}

_logger.Information($"User scanning finished. Total enabled users found: '{myDomainUsers.Count}'.");

return myDomainUsers;
}
private static List<UserPrincipalExtension> GetCelebratingUsers(List<UserPrincipalExtension> allUsers)
{
_logger.Information("Filtering users whose birthday is equal today");

var celebratingUsers = new List<UserPrincipalExtension>();

foreach(var user in allUsers)
{
if(!String.IsNullOrEmpty(user.ExtensionAttribute3))
{
// Trying to deserialize extensionAttribute3 and parse Date of birth (see Ex3.cs).
var ex3 = JsonSerializer.Deserialize<Ex3>(user.ExtensionAttribute3);

// If DateTime was parsed from string succesfully.
if(ex3.Dob != DateTime.MinValue)
{
// Add user to the list if he has a birthday today.
// Comparing day and month would respect leap years.
if(ex3.Dob.Month == DateTime.Now.Month && ex3.Dob.Day == DateTime.Now.Day)
{
celebratingUsers.Add(user);
}
}
}
}

_logger.Information($"Users count who has a birthday today: '{celebratingUsers.Count}'.");

return celebratingUsers;
}
private static IConfigurationRoot LoadConfiguration(string configJsonFile)
{
var builder = new ConfigurationBuilder()
Expand All @@ -110,6 +48,7 @@ private static IConfigurationRoot LoadConfiguration(string configJsonFile)
return config;

}

private static Logger InitLogger(IConfigurationRoot config)
{
// If logger info is not defined then use working dir
Expand All @@ -123,95 +62,5 @@ private static Logger InitLogger(IConfigurationRoot config)

return _logger;
}
private static async Task GenerateEmailsAsync(List<UserPrincipalExtension> sendTos)
{
// List of task per user in list.
List<Task> listOfTasks = new List<Task>();

// Reading configuration from appsettings.json
var mailFrom = new EmailAddress(_config.GetSection("SendGrid:SenderEmail").Value,
_config.GetSection("SendGrid:SenderName").Value);
string mailSubject = _config.GetSection("SendGrid:MailSubject").Value;
string ccRecipient = _config.GetSection("SendGrid:CcRecipient").Value;
bool SimulationModeEnabled = bool.Parse(_config.GetSection("SendGrid:SimulationModeEnabled").Value);
string mailTemplate;

try
{
// Read file from mail template.
mailTemplate = File.ReadAllText(_config.GetSection("SendGrid:MailTemplateHtml").Value);
}
catch (Exception e)
{
_logger.Error($"Unable to read mail template file: '{_config.GetSection("SendGrid:MailTemplateHtml").Value}'. Exception: '{e.Message}'.");

return;
}

if(SimulationModeEnabled)
{
_logger.Information("--- Simulation mode turned ON. No email will be sent to the users. You can turn it off in appsettings.json");
}

foreach(var user in sendTos)
{
// Replacing message subject and body with user display name.
var compiledMailTemplate = mailTemplate.Replace("{{name}}", user.DisplayName);
var compiledMailSubject = mailSubject.Replace("{{name}}", user.DisplayName);

// If user has an email address.
if (!String.IsNullOrEmpty(user.EmailAddress))
{
var mail = new SendGridMessage
{
From = mailFrom,
Subject = compiledMailSubject,
PlainTextContent = compiledMailTemplate,
HtmlContent = compiledMailTemplate
};

// Adding user email to the 'To' field.
mail.AddTo(user.EmailAddress);

// If we have recipient to add to CC.
if (!string.IsNullOrEmpty(ccRecipient))
{
mail.AddCc(ccRecipient);
}

// If simulation mode is enabled just log message and continue.
if(SimulationModeEnabled)
{
_logger.Information($"E-mail won't sent to '{user.EmailAddress}'.");
continue;
}

// Adding task to the list.
listOfTasks.Add(SendBrthAsync(mail, user));
}
else
{
_logger.Warning($"User '{user.UserPrincipalName}' does not have a valid email. Email: '{user.EmailAddress}'.");
}
}
// Wainting when all tasks are done.
await Task.WhenAll(listOfTasks);
}
private static async Task SendBrthAsync(SendGridMessage mail, UserPrincipalExtension user)
{
// Creating client and sending messages.
var client = new SendGridClient(_config.GetSection("SendGrid:ApiKey").Value);
var result = await client.SendEmailAsync(mail);

// Logging results
if (result.StatusCode != HttpStatusCode.Accepted)
{
_logger.Error($"Unable send email to: '{user.EmailAddress}'. Status code '{result.StatusCode}'.");
}
else if (result.StatusCode == HttpStatusCode.Accepted)
{
_logger.Information($"Mail has been sent to: '{user.EmailAddress}'.");
}
}
}
}
Loading

0 comments on commit 8ca0c44

Please sign in to comment.