Skip to content

Commit 2174a36

Browse files
authored
Merge pull request #89 from Lombiq/issue/OSOE-693
OSOE-693: Display and send warnings also when 80 and 90% of the e-mail quota is reached
2 parents 8456b59 + c5e3c0b commit 2174a36

25 files changed

+501
-329
lines changed

Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI/Extensions/TestCaseUITestContextExtensions.cs

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
using Lombiq.Tests.UI.Helpers;
33
using Lombiq.Tests.UI.Services;
44
using OpenQA.Selenium;
5+
using Shouldly;
56
using System;
7+
using System.Collections.Generic;
68
using System.Threading.Tasks;
79

810
namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Tests.UI.Extensions;
@@ -11,7 +13,8 @@ public static class TestCaseUITestContextExtensions
1113
{
1214
private const string SuccessfulSubject = "Successful test message";
1315
private const string UnSuccessfulSubject = "Unsuccessful test message";
14-
private const string DashboardWarning =
16+
private const string WarningSubject = "[Warning] Your site has used";
17+
private const string DashboardExceededMessage =
1518
"//p[contains(@class,'alert-danger')][contains(.,'It seems that your site sent out more e-mails')]";
1619

1720
public static async Task TestEmailQuotaManagementBehaviorAsync(
@@ -21,26 +24,57 @@ public static async Task TestEmailQuotaManagementBehaviorAsync(
2124
{
2225
await context.SignInDirectlyAndGoToDashboardAsync();
2326

24-
context.Missing(By.XPath(DashboardWarning));
27+
context.Missing(By.XPath(DashboardExceededMessage));
2528

2629
await context.GoToAdminRelativeUrlAsync("/Settings/email");
2730

2831
CheckEmailsSentWarningMessage(context, exists: moduleShouldInterfere, maximumEmailQuota, 0);
2932

30-
await SendTestEmailAsync(context, SuccessfulSubject);
31-
context.SuccessMessageExists();
33+
var warningEmails = new List<int>();
34+
for (int i = 0; i < maximumEmailQuota; i++)
35+
{
36+
await SendTestEmailAsync(context, SuccessfulSubject);
37+
context.SuccessMessageExists();
38+
CheckEmailsSentWarningMessage(context, exists: moduleShouldInterfere, maximumEmailQuota, i + 1);
39+
var warningLevel = Convert.ToInt32(Math.Round((double)(i + 1) / maximumEmailQuota * 100, 0));
3240

33-
CheckEmailsSentWarningMessage(context, exists: moduleShouldInterfere, maximumEmailQuota, 1);
41+
if (!moduleShouldInterfere) continue;
3442

35-
await context.GoToDashboardAsync();
36-
context.CheckExistence(By.XPath(DashboardWarning), exists: moduleShouldInterfere);
43+
if (warningLevel >= 100)
44+
{
45+
await context.GoToDashboardAsync();
46+
context.CheckExistence(By.XPath(DashboardExceededMessage), exists: true);
47+
}
48+
else if (warningLevel >= 80)
49+
{
50+
await context.GoToDashboardAsync();
51+
context.CheckExistence(
52+
By.XPath($"//p[contains(@class,'alert-warning')]" +
53+
$"[contains(.,'It seems that your site sent out {warningLevel.ToTechnicalString()}% of e-mail')]"),
54+
exists: true);
55+
if (!warningEmails.Contains(warningLevel))
56+
{
57+
warningEmails.Add(warningLevel);
58+
}
59+
}
60+
}
3761

3862
await SendTestEmailAsync(context, UnSuccessfulSubject);
3963
await context.GoToSmtpWebUIAsync();
4064
context.CheckExistence(ByHelper.SmtpInboxRow(SuccessfulSubject), exists: true);
4165
context.CheckExistence(
42-
ByHelper.SmtpInboxRow("[Action Required] Your DotNest site has run over its e-mail quota"),
66+
ByHelper.SmtpInboxRow("[Action Required] Your site has run over its e-mail quota"),
67+
exists: moduleShouldInterfere);
68+
var warningMessageExists = context.CheckExistence(
69+
ByHelper.SmtpInboxRow(WarningSubject),
4370
exists: moduleShouldInterfere);
71+
if (moduleShouldInterfere && warningMessageExists)
72+
{
73+
(context.GetAll(
74+
ByHelper.SmtpInboxRow(WarningSubject)).Count == warningEmails.Count)
75+
.ShouldBeTrue();
76+
}
77+
4478
context.CheckExistence(ByHelper.SmtpInboxRow(UnSuccessfulSubject), exists: !moduleShouldInterfere);
4579
}
4680

Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/EmailQuotaErrorFilter.cs renamed to Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/DashboardQuotaFilter.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99

1010
namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Filters;
1111

12-
public class EmailQuotaErrorFilter : IAsyncResultFilter
12+
public class DashboardQuotaFilter : IAsyncResultFilter
1313
{
1414
private readonly IShapeFactory _shapeFactory;
1515
private readonly ILayoutAccessor _layoutAccessor;
16-
private readonly IQuotaService _quotaService;
16+
private readonly IEmailQuotaService _emailQuotaService;
1717

18-
public EmailQuotaErrorFilter(
18+
public DashboardQuotaFilter(
1919
IShapeFactory shapeFactory,
2020
ILayoutAccessor layoutAccessor,
21-
IQuotaService quotaService)
21+
IEmailQuotaService emailQuotaService)
2222
{
2323
_shapeFactory = shapeFactory;
2424
_layoutAccessor = layoutAccessor;
25-
_quotaService = quotaService;
25+
_emailQuotaService = emailQuotaService;
2626
}
2727

2828
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
@@ -41,13 +41,25 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE
4141
actionRouteArea == $"{nameof(OrchardCore)}.{nameof(OrchardCore.Admin)}" &&
4242
actionRouteValue is nameof(AdminController.Index) &&
4343
context.Result is ViewResult &&
44-
_quotaService.ShouldLimitEmails() &&
45-
(await _quotaService.IsQuotaOverTheLimitAsync()).IsOverQuota)
44+
_emailQuotaService.ShouldLimitEmails())
4645
{
4746
var layout = await _layoutAccessor.GetLayoutAsync();
4847
var contentZone = layout.Zones["Content"];
48+
var currentEmailQuota = await _emailQuotaService.IsQuotaOverTheLimitAsync();
4949

50-
await contentZone.AddAsync(await _shapeFactory.CreateAsync("EmailQuotaError"), "0");
50+
var currentUsagePercentage = currentEmailQuota.EmailQuota
51+
.CurrentUsagePercentage(_emailQuotaService.GetEmailQuotaPerMonth());
52+
53+
if (currentUsagePercentage >= 80)
54+
{
55+
await contentZone.AddAsync(
56+
await _shapeFactory.CreateAsync("DashboardQuotaMessage", new
57+
{
58+
currentEmailQuota.IsOverQuota,
59+
UsagePercentage = currentUsagePercentage,
60+
}),
61+
"0");
62+
}
5163
}
5264

5365
await next();

Lombiq.Hosting.Tenants.EmailQuotaManagement/Filters/EmailSettingsQuotaFilter.cs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
using Lombiq.Hosting.Tenants.EmailQuotaManagement.Models;
21
using Lombiq.Hosting.Tenants.EmailQuotaManagement.Services;
32
using Microsoft.AspNetCore.Mvc;
43
using Microsoft.AspNetCore.Mvc.Filters;
5-
using Microsoft.Extensions.Options;
64
using OrchardCore.DisplayManagement;
75
using OrchardCore.DisplayManagement.Layout;
86
using OrchardCore.Mvc.Core.Utilities;
@@ -15,19 +13,16 @@ public class EmailSettingsQuotaFilter : IAsyncResultFilter
1513
{
1614
private readonly IShapeFactory _shapeFactory;
1715
private readonly ILayoutAccessor _layoutAccessor;
18-
private readonly IQuotaService _quotaService;
19-
private readonly EmailQuotaOptions _emailQuotaOptions;
16+
private readonly IEmailQuotaService _emailQuotaService;
2017

2118
public EmailSettingsQuotaFilter(
2219
IShapeFactory shapeFactory,
2320
ILayoutAccessor layoutAccessor,
24-
IQuotaService quotaService,
25-
IOptions<EmailQuotaOptions> emailQuotaOptions)
21+
IEmailQuotaService emailQuotaService)
2622
{
2723
_shapeFactory = shapeFactory;
2824
_layoutAccessor = layoutAccessor;
29-
_quotaService = quotaService;
30-
_emailQuotaOptions = emailQuotaOptions.Value;
25+
_emailQuotaService = emailQuotaService;
3126
}
3227

3328
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
@@ -48,17 +43,17 @@ actionRouteValue is nameof(AdminController.Index) &&
4843
context.Result is ViewResult &&
4944
context.RouteData.Values.TryGetValue("GroupId", out var groupId) &&
5045
(string)groupId == "email" &&
51-
_quotaService.ShouldLimitEmails())
46+
_emailQuotaService.ShouldLimitEmails())
5247
{
5348
var layout = await _layoutAccessor.GetLayoutAsync();
5449
var contentZone = layout.Zones["Content"];
5550

56-
var quota = await _quotaService.GetCurrentQuotaAsync();
51+
var quota = await _emailQuotaService.GetOrCreateCurrentQuotaAsync();
5752
await contentZone.AddAsync(
58-
await _shapeFactory.CreateAsync("EmailSettingsQuota", new
53+
await _shapeFactory.CreateAsync("EmailSettingsQuotaMessage", new
5954
{
60-
CurrentEmailCount = quota.CurrentEmailQuotaCount,
61-
EmailQuota = _emailQuotaOptions.EmailQuotaPerMonth,
55+
quota.CurrentEmailUsageCount,
56+
EmailQuotaPerMonth = _emailQuotaService.GetEmailQuotaPerMonth(),
6257
}),
6358
"0");
6459
}

Lombiq.Hosting.Tenants.EmailQuotaManagement/Indexes/EmailQuotaIndex.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Indexes;
66

77
public class EmailQuotaIndex : MapIndex
88
{
9-
public int CurrentEmailQuotaCount { get; set; }
10-
public DateTime LastReminder { get; set; }
9+
public int CurrentEmailUsageCount { get; set; }
10+
public DateTime LastReminderUtc { get; set; }
11+
public int LastReminderPercentage { get; set; }
1112
}
1213

1314
public class EmailQuotaIndexProvider : IndexProvider<EmailQuota>
@@ -16,7 +17,8 @@ public override void Describe(DescribeContext<EmailQuota> context) =>
1617
context.For<EmailQuotaIndex>()
1718
.Map(emailQuota => new EmailQuotaIndex
1819
{
19-
CurrentEmailQuotaCount = emailQuota.CurrentEmailQuotaCount,
20-
LastReminder = emailQuota.LastReminder,
20+
CurrentEmailUsageCount = emailQuota.CurrentEmailUsageCount,
21+
LastReminderUtc = emailQuota.LastReminderUtc,
22+
LastReminderPercentage = emailQuota.LastReminderPercentage,
2123
});
2224
}

Lombiq.Hosting.Tenants.EmailQuotaManagement/Migrations/EmailQuotaMigrations.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@ public class EmailQuotaMigrations : DataMigration
1010
public int Create()
1111
{
1212
SchemaBuilder.CreateMapIndexTable<EmailQuotaIndex>(
13-
table => table.Column<int>(nameof(EmailQuotaIndex.CurrentEmailQuotaCount))
14-
.Column<DateTime>(nameof(EmailQuotaIndex.LastReminder)));
13+
table => table.Column<int>(nameof(EmailQuotaIndex.CurrentEmailUsageCount))
14+
.Column<DateTime>(nameof(EmailQuotaIndex.LastReminderUtc))
15+
.Column<int>(nameof(EmailQuotaIndex.LastReminderPercentage)));
1516

16-
return 1;
17+
return 2;
18+
}
19+
20+
public int UpdateFrom1()
21+
{
22+
SchemaBuilder.AlterTable(nameof(EmailQuotaIndex), table => table
23+
.AddColumn<int>(nameof(EmailQuotaIndex.LastReminderPercentage))
24+
);
25+
26+
return 2;
1727
}
1828
}

Lombiq.Hosting.Tenants.EmailQuotaManagement/Models/EmailQuota.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ namespace Lombiq.Hosting.Tenants.EmailQuotaManagement.Models;
44

55
public class EmailQuota
66
{
7-
public int CurrentEmailQuotaCount { get; set; }
8-
public DateTime LastReminder { get; set; }
7+
public int CurrentEmailUsageCount { get; set; }
8+
public DateTime LastReminderUtc { get; set; }
9+
public int LastReminderPercentage { get; set; }
10+
11+
public int CurrentUsagePercentage(int emailQuotaPerMonth) =>
12+
Convert.ToInt32(Math.Round((double)CurrentEmailUsageCount / emailQuotaPerMonth * 100, 0));
913
}

Lombiq.Hosting.Tenants.EmailQuotaManagement/Services/EmailQuotaEmailService.cs

Lines changed: 0 additions & 43 deletions
This file was deleted.

Lombiq.Hosting.Tenants.EmailQuotaManagement/Services/EmailQuotaResetTask.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ public class EmailQuotaResetBackgroundTask : IBackgroundTask
1313
{
1414
public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
1515
{
16-
var quotaService = serviceProvider.GetRequiredService<IQuotaService>();
17-
var currentQuota = await quotaService.GetCurrentQuotaAsync();
18-
quotaService.ResetQuota(currentQuota);
16+
var emailQuotaService = serviceProvider.GetRequiredService<IEmailQuotaService>();
17+
var currentQuota = await emailQuotaService.GetOrCreateCurrentQuotaAsync();
18+
emailQuotaService.ResetQuota(currentQuota);
1919
}
2020
}

0 commit comments

Comments
 (0)