Skip to content

Commit

Permalink
Merge pull request #411 from ucdavis/JCS/EmailStubs
Browse files Browse the repository at this point in the history
Email stubs
  • Loading branch information
jSylvestre authored Jul 31, 2024
2 parents bd0bc27 + 5b94f6a commit 758fe43
Show file tree
Hide file tree
Showing 12 changed files with 378 additions and 28 deletions.
8 changes: 5 additions & 3 deletions Hippo.Core/Services/EmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ public async Task SendEmail(EmailModel emailModel)
{
message.To.Add(new MailAddress(email, email));
}

foreach (var ccEmail in emailModel.CcEmails)
if (emailModel.CcEmails != null)
{
message.CC.Add(new MailAddress(ccEmail));
foreach (var ccEmail in emailModel.CcEmails)
{
message.CC.Add(new MailAddress(ccEmail));
}
}

if (!string.IsNullOrWhiteSpace(_emailSettings.BccEmail))
Expand Down
137 changes: 123 additions & 14 deletions Hippo.Core/Services/NotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,43 +20,36 @@ namespace Hippo.Core.Services
public interface INotificationService
{
Task<bool> AccountRequest(Request request);
Task<bool> AccountDecision(Request request, bool isApproved, string overrideSponsor = null, string reason = null);
Task<bool> AccountDecision(Request request, bool isApproved, string decidedBy , string reason = null);
Task<bool> AdminOverrideDecision(Request request, bool isApproved, User adminUser, string reason = null);
Task<bool> SimpleNotification(SimpleNotificationModel simpleNotificationModel, string[] emails, string[] ccEmails = null);

Task<bool> AdminPaymentFailureNotification(string[] emails, string clusterName, int[] orderIds);
Task<bool> SponsorPaymentFailureNotification(string[] emails, Order order); //Could possibly just pass the order Id, but there might be more order info we want to include
Task<bool> OrderNotification(SimpleNotificationModel simpleNotificationModel, Order order, string[] emails, string[] ccEmails = null);
}

public class NotificationService : INotificationService
{
private readonly AppDbContext _dbContext;
private readonly IEmailService _emailService;
private readonly EmailSettings _emailSettings;
private readonly IUserService _userService;
private readonly IMjmlRenderer _mjmlRenderer;

public NotificationService(AppDbContext dbContext, IEmailService emailService,
IOptions<EmailSettings> emailSettings, IUserService userService, IMjmlRenderer mjmlRenderer)
IOptions<EmailSettings> emailSettings, IMjmlRenderer mjmlRenderer)
{
_dbContext = dbContext;
_emailService = emailService;
_emailSettings = emailSettings.Value;
_userService = userService;
_mjmlRenderer = mjmlRenderer;
}

public async Task<bool> AccountDecision(Request request, bool isApproved, string overrideDecidedBy = null, string details = "")
public async Task<bool> AccountDecision(Request request, bool isApproved, string decidedBy, string details = "")
{

try
{
var decidedBy = String.Empty;
if (!string.IsNullOrWhiteSpace(overrideDecidedBy))
{
decidedBy = overrideDecidedBy;
}
else
{
decidedBy = (await _userService.GetCurrentUser()).Name;
}

var requestUrl = $"{_emailSettings.BaseUrl}/{request.Cluster.Name}"; //TODO: Only have button if approved?
var emailTo = request.Requester.Email;
Expand Down Expand Up @@ -237,5 +230,121 @@ private async Task<string[]> GetGroupAdminEmails(Group group)
.ToArrayAsync();
return groupAdminEmails;
}

public async Task<bool> AdminPaymentFailureNotification(string[] emails, string clusterName, int[] orderIds)
{
try
{
var message = "The payment for one or more orders in hippo have failed.";

var model = new OrderNotificationModel()
{
UcdLogoUrl = $"{_emailSettings.BaseUrl}/media/caes-logo-gray.png",
Subject = "Payment failed",
Header = "Order Payment Failed",
Paragraphs = new List<string>(),
};
foreach (var orderId in orderIds)
{
model.Paragraphs.Add($"{_emailSettings.BaseUrl}/{clusterName}/order/details/{orderId}");

}

var htmlBody = await _mjmlRenderer.RenderView("/Views/Emails/OrderAdminPaymentFail_mjml.cshtml", model);

await _emailService.SendEmail(new EmailModel
{
Emails = emails,
CcEmails = null,
HtmlBody = htmlBody,
TextBody = message,
Subject = model.Subject,
});

return true;
}
catch (Exception ex)
{
Log.Error("Error emailing Sponsor Payment Failure Notification", ex);
return false;
}
}

public async Task<bool> SponsorPaymentFailureNotification(string[] emails, Order order)
{
try
{
var message = "The payment for the following order has failed. Please update your billing information in Hippo.";

var model = new OrderNotificationModel()
{
UcdLogoUrl = $"{_emailSettings.BaseUrl}/media/caes-logo-gray.png",
ButtonUrl = $"{_emailSettings.BaseUrl}/{order.Cluster.Name}/order/details/{order.Id}",
Subject = "Payment failed",
Header = "Order Payment Failed",
Paragraphs = new List<string>(),
};
model.Paragraphs.Add($"Order: {order.Name}");
model.Paragraphs.Add($"Order Id: {order.Id}");
model.Paragraphs.Add("The payment for this order has failed.");
model.Paragraphs.Add("This is most likely due to a Aggie Enterprise Chart String which is no longer valid.");
model.Paragraphs.Add("The order details will have the validation message from Aggie Enterprise.");
model.Paragraphs.Add("Please update your billing information in Hippo.");

var htmlBody = await _mjmlRenderer.RenderView("/Views/Emails/OrderNotification_mjml.cshtml", model);

await _emailService.SendEmail(new EmailModel
{
Emails = emails,
CcEmails = null,
HtmlBody = htmlBody,
TextBody = message,
Subject = model.Subject,
});

return true;
}
catch (Exception ex)
{
Log.Error("Error emailing Sponsor Payment Failure Notification", ex);
return false;
}
}

public async Task<bool> OrderNotification(SimpleNotificationModel simpleNotificationModel, Order order, string[] emails, string[] ccEmails = null)
{
try
{
var message = simpleNotificationModel.Paragraphs.FirstOrDefault();

var model = new OrderNotificationModel()
{
UcdLogoUrl = $"{_emailSettings.BaseUrl}/media/caes-logo-gray.png",
ButtonUrl = $"{_emailSettings.BaseUrl}/{order.Cluster.Name}/order/details/{order.Id}",
Subject = simpleNotificationModel.Subject,
Header = simpleNotificationModel.Header,
Paragraphs = simpleNotificationModel.Paragraphs,
};


var htmlBody = await _mjmlRenderer.RenderView("/Views/Emails/OrderNotification_mjml.cshtml", model);

await _emailService.SendEmail(new EmailModel
{
Emails = emails,
CcEmails = ccEmails,
HtmlBody = htmlBody,
TextBody = message,
Subject = simpleNotificationModel.Subject,
});

return true;
}
catch (Exception ex)
{
Log.Error("Error emailing Order Notification", ex);
return false;
}
}
}
}
24 changes: 22 additions & 2 deletions Hippo.Core/Services/PaymentsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ public class PaymentsService : IPaymentsService
private readonly AppDbContext _dbContext;
private readonly IHistoryService _historyService;
private readonly IAggieEnterpriseService _aggieEnterpriseService;
private readonly INotificationService _notificationService;

public PaymentsService(AppDbContext dbContext, IHistoryService historyService, IAggieEnterpriseService aggieEnterpriseService)
public PaymentsService(AppDbContext dbContext, IHistoryService historyService, IAggieEnterpriseService aggieEnterpriseService, INotificationService notificationService)
{
_dbContext = dbContext;
_historyService = historyService;
_aggieEnterpriseService = aggieEnterpriseService;
_notificationService = notificationService;
}
public async Task<bool> CreatePayments()
{
Expand Down Expand Up @@ -164,8 +166,17 @@ public async Task<bool> NotifyAboutFailedPayments()
}
if (invalidChartStrings)
{
Log.Error("Order {0} has an invalid chart string", order.Id);
//TODO: Notify the sponsor
//Remember to add notification/email service to job
try
{
await _notificationService.SponsorPaymentFailureNotification(new string[] { order.PrincipalInvestigator.Email }, order);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to notify sponsor for order {0}", order.Id);
}


invalidOrderIdsInCluster.Add(order.Id);
Expand All @@ -177,9 +188,18 @@ public async Task<bool> NotifyAboutFailedPayments()
//Send email to cluster admins with list of orders that have failed payments
//https://localhost:44371/caesfarm/order/details/46

var clusterAdmins = await _dbContext.Users.AsNoTracking().Where(u => u.Permissions.Any(p => p.Cluster.Id == orderGroup.Key && p.Role.Name == Role.Codes.ClusterAdmin)).OrderBy(u => u.LastName).ThenBy(u => u.FirstName).ToArrayAsync();
var clusterAdmins = await _dbContext.Users.AsNoTracking().Where(u => u.Permissions.Any(p => p.Cluster.Id == orderGroup.Key && p.Role.Name == Role.Codes.ClusterAdmin)).Select(a => a.Email).ToArrayAsync();
//TODO: Notify the cluster admins with a single email

try
{
await _notificationService.AdminPaymentFailureNotification(clusterAdmins, cluster.Name, invalidOrderIdsInCluster.ToArray());
}
catch (Exception ex)
{
Log.Error(ex, "Failed to notify cluster admins for cluster {0}", cluster.Id);
}

}

}
Expand Down
2 changes: 1 addition & 1 deletion Hippo.Email/Hippo.Email.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Razor.Templating.Core" Version="1.7.0" />
Expand Down
22 changes: 22 additions & 0 deletions Hippo.Email/Models/OrderNotificationModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hippo.Email.Models
{
public class OrderNotificationModel
{

public string UcdLogoUrl { get; set; } = String.Empty;

public string ButtonText { get; set; } = "View Order";
public string ButtonUrl { get; set; } = "";

public string Subject { get; set; } = "";
public string Header { get; set; } = "";
public List<string> Paragraphs { get; set; } = new();

}
}
43 changes: 43 additions & 0 deletions Hippo.Email/Views/Emails/OrderAdminPaymentFail_mjml.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@using Hippo.Email.Models
@model Hippo.Email.Models.OrderNotificationModel

@{
ViewData["EmailTitle"] = "Order Notification";
Layout = "_EmailLayout_mjml";
}


<mj-section border-top="4px solid #481268" border-left="1px solid #c7c8cc" border-right="1px solid #c7c8cc"
background-color="#ffffff" padding-bottom="20px" padding-top="20px">
<mj-column width="100%" vertical-align="top">
<mj-text padding-top="0px" padding-bottom="0px" font-size="28px" color="#1F1F1F">
<p>@Model.Header</p>
</mj-text>
<mj-text padding-top="0px" padding-bottom="0px" font-size="24px" color="rgba(31,31,31,0.8)">
<p>There are one or more orders that have failing payments.</p>
<p>The Sponsor has been notified, and until the billing is corrected the payment can't go through.</p>
</mj-text>
</mj-column>
</mj-section>


<mj-section border-left="1px solid #c7c8cc" border-right="1px solid #c7c8cc" background-color="#ffffff"
padding-bottom="0px" padding-top="20px">
<mj-column width="90%" vertical-align="top">
<mj-text padding-top="0px" padding-left="0px" padding-bottom="0px">
<h2>Orders With Failing Payments</h2>
</mj-text>
</mj-column>
</mj-section>
<mj-section border-left="1px solid #c7c8cc" border-right="1px solid #c7c8cc" background-color="#ffffff"
padding-bottom="0px" padding-top="10px">
<mj-column width="90%" vertical-align="top" border-left="4px solid #481268" padding-left="10px">
<mj-text line-height="1.5" padding-top="0px" padding-left="0px" padding-bottom="5px">
@foreach (var paragraph in @Model.Paragraphs)
{
<p><a href="@paragraph">@paragraph</a></p>
}
</mj-text>
</mj-column>
</mj-section>

27 changes: 27 additions & 0 deletions Hippo.Email/Views/Emails/OrderNotification_mjml.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@using Hippo.Email.Models
@model Hippo.Email.Models.OrderNotificationModel

@{
ViewData["EmailTitle"] = "Order Notification";
Layout = "_EmailLayout_mjml";
}


<mj-section border-top="4px solid #481268" border-left="1px solid #c7c8cc" border-right="1px solid #c7c8cc"
background-color="#ffffff" padding-bottom="20px" padding-top="20px">
<mj-column width="100%" vertical-align="top">
<mj-text padding-top="0px" padding-bottom="0px" font-size="28px" color="#1F1F1F">
<p>@Model.Header</p>
</mj-text>
<mj-text padding-top="0px" padding-bottom="0px" font-size="24px" color="rgba(31,31,31,0.8)">
@foreach (var paragraph in @Model.Paragraphs)
{
<p>@paragraph</p>
}
</mj-text>
</mj-column>
</mj-section>

@await Html.PartialAsync("_EmailButton_mjml", new EmailButtonModel(@Model!.ButtonText, @Model!.ButtonUrl))


7 changes: 6 additions & 1 deletion Hippo.Jobs.OrderProcess/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mjml.Net;
using Serilog;


Expand Down Expand Up @@ -120,14 +121,18 @@ private static ServiceProvider ConfigureServices()
services.Configure<AzureSettings>(Configuration.GetSection("Azure"));
services.Configure<SlothSettings>(Configuration.GetSection("Sloth"));
services.Configure<AggieEnterpriseSettings>(Configuration.GetSection("AggieEnterprise"));
services.Configure<EmailSettings>(Configuration.GetSection("Email"));

services.AddScoped<IPaymentsService, PaymentsService>();
services.AddTransient<IAggieEnterpriseService, AggieEnterpriseService>();
services.AddSingleton<IHistoryService, HistoryService>();
services.AddHttpClient();
services.AddSingleton<ISecretsService, SecretsService>();
services.AddScoped<ISlothService, SlothService>();
//TODO: This will probably need the notification service as well.
services.AddScoped<INotificationService, NotificationService>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<IMjmlRenderer, MjmlRenderer>();



return services.BuildServiceProvider();
Expand Down
10 changes: 10 additions & 0 deletions Hippo.Jobs.OrderProcess/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@
"ApiUrl": "https://sloth-api-test.azurewebsites.net/v2/",
"HippoBaseUrl": "https://hippo-test.azurewebsites.net/"
},
"Email": {
"ApiKey": "[External]",
"UserName": "[External]",
"Password": "[External]",
"Host": "[External]",
"Port": 1,
"FromEmail": "[email protected]",
"FromName": "Hippo Notification",
"BaseUrl": "https://localhost:44371"
},
"ConnectionStrings": {
"DefaultConnection": "[External]"
}
Expand Down
Loading

0 comments on commit 758fe43

Please sign in to comment.