diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemable.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemable.html.hbs
new file mode 100644
index 000000000..d233381b9
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemable.html.hbs
@@ -0,0 +1,16 @@
+{{#>FullHtmlLayout}}
+
+
+
+ {{OrganizationName}} has offered to sponsor a family organization for you with Bitwarden.
+ |
+
+
+
+
+ Redeem
+
+ |
+
+
+{{/FullHtmlLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemable.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemable.text.hbs
new file mode 100644
index 000000000..a6bd7bb7e
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemable.text.hbs
@@ -0,0 +1,5 @@
+{{#>BasicTextLayout}}
+{{OrganizationName}} has offered to sponsor a family organization for you with Bitwarden. To redeem please click the following link:
+
+{{Url}}
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUser.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUser.html.hbs
new file mode 100644
index 000000000..346982ab2
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUser.html.hbs
@@ -0,0 +1,9 @@
+{{#>FullHtmlLayout}}
+
+
+
+ You have redeemed a family organization sponsorship from {{OrganizationName}}.
+ |
+
+
+{{/FullHtmlLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUser.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUser.text.hbs
new file mode 100644
index 000000000..994c88358
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUser.text.hbs
@@ -0,0 +1,3 @@
+{{#>BasicTextLayout}}
+You have redeemed a family organization sponsorship from {{OrganizationName}}.
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUser.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUser.html.hbs
new file mode 100644
index 000000000..f2615c374
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUser.html.hbs
@@ -0,0 +1,9 @@
+{{#>FullHtmlLayout}}
+
+
+
+ You have redeemed a Families for Enterprise sponsorship from {{OrganizationName}}.
+ |
+
+
+{{/FullHtmlLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUser.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUser.text.hbs
new file mode 100644
index 000000000..8dd8e05ef
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUser.text.hbs
@@ -0,0 +1,3 @@
+{{#>BasicTextLayout}}
+A user in your organization has redeemed a family invitation.
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequired.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequired.html.hbs
new file mode 100644
index 000000000..51750c24f
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequired.html.hbs
@@ -0,0 +1,16 @@
+{{#>FullHtmlLayout}}
+
+
+
+ Your Families for Enterprise sponsorship requires reconfirmation.
+ |
+
+
+
+
+ Reconfirm
+
+ |
+
+
+{{/FullHtmlLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequired.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequired.text.hbs
new file mode 100644
index 000000000..82bb6387e
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequired.text.hbs
@@ -0,0 +1,5 @@
+{{#>BasicTextLayout}}
+Your Families for Enterprise sponsorship requires reconfirmation. To redeem please click the following link:
+
+{{Url}}
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs
new file mode 100644
index 000000000..b5d45565a
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs
@@ -0,0 +1,9 @@
+{{#>FullHtmlLayout}}
+
+
+
+ Your Families for Enterprise sponsorship has ended and you will lose premium access at the end of the current billing cycle.
+ |
+
+
+{{/FullHtmlLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs
new file mode 100644
index 000000000..a2c98521f
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs
@@ -0,0 +1,3 @@
+{{#>BasicTextLayout}}
+Your Families for Enterprise sponsorship has ended and you will lose premium access at the end of the current billing cycle.
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipReverting.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipReverting.html.hbs
new file mode 100644
index 000000000..0b3f90eae
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipReverting.html.hbs
@@ -0,0 +1,9 @@
+{{#>FullHtmlLayout}}
+
+
+
+ Your Families for Enterprise sponsorship will revert back to your existing payment method at the end of the current billing cycle.
+ |
+
+
+{{/FullHtmlLayout}}
\ No newline at end of file
diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipReverting.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipReverting.text.hbs
new file mode 100644
index 000000000..16017d79a
--- /dev/null
+++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipReverting.text.hbs
@@ -0,0 +1,3 @@
+{{#>BasicTextLayout}}
+Your Families for Enterprise sponsorship will revert back to your existing payment method at the end of the current billing cycle.
+{{/BasicTextLayout}}
\ No newline at end of file
diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemableViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemableViewModel.cs
new file mode 100644
index 000000000..57e01e710
--- /dev/null
+++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemableViewModel.cs
@@ -0,0 +1,8 @@
+namespace Bit.Core.Models.Mail.FamiliesForEnterprise
+{
+ public class FamiliesForEnterpriseInviteRedeemableViewModel : BaseMailModel
+ {
+ public string OrganizationName { get; set; }
+ public string Url { get; set; }
+ }
+}
diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUserViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUserViewModel.cs
new file mode 100644
index 000000000..bda9c907c
--- /dev/null
+++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToFamilyUserViewModel.cs
@@ -0,0 +1,7 @@
+namespace Bit.Core.Models.Mail.FamiliesForEnterprise
+{
+ public class FamiliesForEnterpriseInviteRedeemedToFamilyUserViewModel : BaseMailModel
+ {
+ public string OrganizationName { get; set; }
+ }
+}
diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUserViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUserViewModel.cs
new file mode 100644
index 000000000..a547b4f35
--- /dev/null
+++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseInviteRedeemedToOrgUserViewModel.cs
@@ -0,0 +1,7 @@
+namespace Bit.Core.Models.Mail.FamiliesForEnterprise
+{
+ public class FamiliesForEnterpriseInviteRedeemedToOrgUserViewModel : BaseMailModel
+ {
+ public string OrganizationName { get; set; }
+ }
+}
diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequiredViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequiredViewModel.cs
new file mode 100644
index 000000000..5cf7ca2e4
--- /dev/null
+++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseReconfirmationRequiredViewModel.cs
@@ -0,0 +1,7 @@
+namespace Bit.Core.Models.Mail.FamiliesForEnterprise
+{
+ public class FamiliesForEnterpriseReconfirmationRequiredViewModel : BaseMailModel
+ {
+
+ }
+}
diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs
new file mode 100644
index 000000000..e6568562a
--- /dev/null
+++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs
@@ -0,0 +1,7 @@
+namespace Bit.Core.Models.Mail.FamiliesForEnterprise
+{
+ public class FamiliesForEnterpriseSponsorshipEndingViewModel : BaseMailModel
+ {
+
+ }
+}
diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipRevertingViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipRevertingViewModel.cs
new file mode 100644
index 000000000..cef96099f
--- /dev/null
+++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipRevertingViewModel.cs
@@ -0,0 +1,7 @@
+namespace Bit.Core.Models.Mail.FamiliesForEnterprise
+{
+ public class FamiliesForEnterpriseSponsorshipRevertingViewModel : BaseMailModel
+ {
+
+ }
+}
diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs
index 8abf5a0d5..c0eb047c1 100644
--- a/src/Core/Services/IMailService.cs
+++ b/src/Core/Services/IMailService.cs
@@ -49,5 +49,13 @@ namespace Bit.Core.Services
Task SendProviderConfirmedEmailAsync(string providerName, string email);
Task SendProviderUserRemoved(string providerName, string email);
Task SendUpdatedTempPasswordEmailAsync(string email, string userName);
+ // TODO: Change signature to hold data needed for email
+ Task SendFamiliesForEnterpriseInviteRedeemableEmailAsync(string email, string organizationName, string token);
+ // NOTE: Not married to these next two names
+ Task SendFamiliesForEnterpriseInviteRedeemedToFamilyUserEmailAsync(string email);
+ Task SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(string email, string organizationName);
+ Task SendFamiliesForEnterpriseReconfirmationRequiredEmailAsync(string email);
+ Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email);
+ Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email);
}
}
diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs
index 8e981f386..ada022a19 100644
--- a/src/Core/Services/Implementations/HandlebarsMailService.cs
+++ b/src/Core/Services/Implementations/HandlebarsMailService.cs
@@ -9,6 +9,7 @@ using System.Net;
using Bit.Core.Utilities;
using System.Linq;
using System.Reflection;
+using Bit.Core.Models.Mail.FamiliesForEnterprise;
using Bit.Core.Models.Mail.Provider;
using Bit.Core.Models.Table.Provider;
using HandlebarsDotNet;
@@ -755,5 +756,93 @@ namespace Bit.Core.Services
message.Category = "UpdatedTempPassword";
await _mailDeliveryService.SendEmailAsync(message);
}
+
+ public async Task SendFamiliesForEnterpriseInviteRedeemableEmailAsync(string email, string organizationName, string token)
+ {
+ // TODO: Complete emails
+ var message = CreateDefaultMessage("A Family Organization Invite Is Redeemable", email);
+
+ // NOTE: If somehow cloud vault changes this will need to change/be injected
+ var url = CoreHelpers.ExtendQuery(new Uri($"https://vault.bitwarden.com/#/sponsored/families-for-enterprise"),
+ new Dictionary
+ {
+ ["sponsorshipToken"] = token,
+ });
+
+ var model = new FamiliesForEnterpriseInviteRedeemableViewModel
+ {
+ Url = url.ToString(),
+ OrganizationName = organizationName,
+ };
+
+ await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseInviteRedeemable", model);
+ message.Category = "FamiliesForEnterpriseInviteRedeemable";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendFamiliesForEnterpriseInviteRedeemedToFamilyUserEmailAsync(string email)
+ {
+ // TODO: Complete emails
+ var message = CreateDefaultMessage("You Have Redeemed A Family Organization Sponsorship", email);
+ var model = new FamiliesForEnterpriseInviteRedeemedToFamilyUserViewModel
+ {
+
+ };
+ await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseInviteRedeemedToFamilyUser", model);
+ message.Category = "FamilyForEnterpriseInviteRedeemedToFamilyUser";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(string email, string organizationName)
+ {
+ // TODO: Complete emails
+ var message = CreateDefaultMessage("A User Has Redeemeed Your Sponsorship", email);
+ var model = new FamiliesForEnterpriseInviteRedeemedToOrgUserViewModel
+ {
+ OrganizationName = organizationName,
+ };
+ await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseInviteRedeemedToOrgUser", model);
+ message.Category = "FamilyForEnterpriseInviteRedeemedToOrgUser";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendFamiliesForEnterpriseReconfirmationRequiredEmailAsync(string email)
+ {
+ // TODO: Complete emails
+ var message = CreateDefaultMessage("Your Sponsorship Requires Reconfirmation", email);
+ var model = new FamiliesForEnterpriseReconfirmationRequiredViewModel
+ {
+
+ };
+ await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseReconfirmationRequired", model);
+ message.Category = "FamiliesForEnterpriseReconfirmationRequired";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email)
+ {
+ // TODO: Complete emails
+ var message = CreateDefaultMessage("A Family Organization Sponsorship Is Reverting", email);
+ var model = new FamiliesForEnterpriseSponsorshipRevertingViewModel
+ {
+
+ };
+ await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseSponsorshipReverting", model);
+ message.Category = "FamiliesForEnterpriseSponsorshipReverting";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
+
+ public async Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email)
+ {
+ // TODO: Complete emails
+ var message = CreateDefaultMessage("A Family Organization Sponsorship Is Ending", email);
+ var model = new FamiliesForEnterpriseSponsorshipEndingViewModel
+ {
+
+ };
+ await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseSponsorshipEnding", model);
+ message.Category = "FamiliesForEnterpriseSponsorshipEnding";
+ await _mailDeliveryService.SendEmailAsync(message);
+ }
}
}
diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs
index f514993d1..7a648a447 100644
--- a/src/Core/Services/NoopImplementations/NoopMailService.cs
+++ b/src/Core/Services/NoopImplementations/NoopMailService.cs
@@ -200,5 +200,35 @@ namespace Bit.Core.Services
{
return Task.FromResult(0);
}
+
+ public Task SendFamiliesForEnterpriseInviteRedeemableEmailAsync(string email, string organizationName, string token)
+ {
+ return Task.FromResult(0);
+ }
+
+ public Task SendFamiliesForEnterpriseInviteRedeemedToFamilyUserEmailAsync(string email)
+ {
+ return Task.FromResult(0);
+ }
+
+ public Task SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(string email, string organizationName)
+ {
+ return Task.FromResult(0);
+ }
+
+ public Task SendFamiliesForEnterpriseReconfirmationRequiredEmailAsync(string email)
+ {
+ return Task.FromResult(0);
+ }
+
+ public Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email)
+ {
+ return Task.FromResult(0);
+ }
+
+ public Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email)
+ {
+ return Task.FromResult(0);
+ }
}
}
diff --git a/test/Core.Test/Services/HandlebarsMailServiceTests.cs b/test/Core.Test/Services/HandlebarsMailServiceTests.cs
index 1204a0613..64b1da193 100644
--- a/test/Core.Test/Services/HandlebarsMailServiceTests.cs
+++ b/test/Core.Test/Services/HandlebarsMailServiceTests.cs
@@ -1,6 +1,14 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Bit.Core.Models.Business;
+using Bit.Core.Models.Table;
+using Bit.Core.Models.Table.Provider;
using Bit.Core.Services;
using Bit.Core.Settings;
+using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
@@ -27,6 +35,131 @@ namespace Bit.Core.Test.Services
);
}
+ [Fact(Skip = "Only for local development")]
+ public async Task SendAllEmails()
+ {
+ // This test is only opt in and is more for development purposes.
+ // This will send all emails to the test email address so that they can be viewed.
+ var namedParameters = new Dictionary<(string, Type), object>
+ {
+ // TODO: Swith to use env variable
+ { ("email", typeof(string)), "test@bitwarden.com" },
+ { ("user", typeof(User)), new User
+ {
+ Id = Guid.NewGuid(),
+ Email = "test@bitwarden.com",
+ }},
+ { ("userId", typeof(Guid)), Guid.NewGuid() },
+ { ("token", typeof(string)), "test_token" },
+ { ("fromEmail", typeof(string)), "test@bitwarden.com" },
+ { ("toEmail", typeof(string)), "test@bitwarden.com" },
+ { ("newEmailAddress", typeof(string)), "test@bitwarden.com" },
+ { ("hint", typeof(string)), "Test Hint" },
+ { ("organizationName", typeof(string)), "Test Organization Name" },
+ { ("orgUser", typeof(OrganizationUser)), new OrganizationUser
+ {
+ Id = Guid.NewGuid(),
+ Email = "test@bitwarden.com",
+ OrganizationId = Guid.NewGuid(),
+
+ }},
+ { ("token", typeof(ExpiringToken)), new ExpiringToken("test_token", DateTime.UtcNow.AddDays(1))},
+ { ("organization", typeof(Organization)), new Organization
+ {
+ Id = Guid.NewGuid(),
+ Name = "Test Organization Name",
+ Seats = 5
+ }},
+ { ("initialSeatCount", typeof(int)), 5},
+ { ("ownerEmails", typeof(IEnumerable)), new [] { "test@bitwarden.com" }},
+ { ("maxSeatCount", typeof(int)), 5 },
+ { ("userIdentifier", typeof(string)), "test_user" },
+ { ("adminEmails", typeof(IEnumerable)), new [] { "test@bitwarden.com" }},
+ { ("returnUrl", typeof(string)), "https://bitwarden.com/" },
+ { ("amount", typeof(decimal)), 1.00M },
+ { ("dueDate", typeof(DateTime)), DateTime.UtcNow.AddDays(1) },
+ { ("items", typeof(List)), new List { "test@bitwarden.com" }},
+ { ("mentionInvoices", typeof(bool)), true },
+ { ("emails", typeof(IEnumerable)), new [] { "test@bitwarden.com" }},
+ { ("deviceType", typeof(string)), "Mobile" },
+ { ("timestamp", typeof(DateTime)), DateTime.UtcNow.AddDays(1)},
+ { ("ip", typeof(string)), "127.0.0.1" },
+ { ("emergencyAccess", typeof(EmergencyAccess)), new EmergencyAccess
+ {
+ Id = Guid.NewGuid(),
+ Email = "test@bitwarden.com",
+ }},
+ { ("granteeEmail", typeof(string)), "test@bitwarden.com" },
+ { ("grantorName", typeof(string)), "Test User" },
+ { ("initiatingName", typeof(string)), "Test" },
+ { ("approvingName", typeof(string)), "Test Name" },
+ { ("rejectingName", typeof(string)), "Test Name" },
+ { ("provider", typeof(Provider)), new Provider
+ {
+ Id = Guid.NewGuid(),
+ }},
+ { ("name", typeof(string)), "Test Name" },
+ { ("ea", typeof(EmergencyAccess)), new EmergencyAccess
+ {
+ Id = Guid.NewGuid(),
+ Email = "test@bitwarden.com",
+ }},
+ { ("userName", typeof(string)), "testUser" },
+ { ("orgName", typeof(string)), "Test Org Name" },
+ { ("providerName", typeof(string)), "testProvider" },
+ { ("providerUser", typeof(ProviderUser)), new ProviderUser
+ {
+ ProviderId = Guid.NewGuid(),
+ Id = Guid.NewGuid(),
+ }},
+ };
+
+ var globalSettings = new GlobalSettings
+ {
+ Mail = new GlobalSettings.MailSettings
+ {
+ Smtp = new GlobalSettings.MailSettings.SmtpSettings
+ {
+ Host = "localhost",
+ TrustServer = true,
+ Port = 10250,
+ },
+ ReplyToEmail = "noreply@bitwarden.com",
+ },
+ SiteName = "Bitwarden",
+ };
+
+ var mailDeliveryService = new MailKitSmtpMailDeliveryService(globalSettings, Substitute.For>());
+
+ var handlebarsService = new HandlebarsMailService(globalSettings, mailDeliveryService, new BlockingMailEnqueuingService());
+
+ var sendMethods = typeof(IMailService).GetMethods(BindingFlags.Public | BindingFlags.Instance)
+ .Where(m => m.Name.StartsWith("Send") && m.Name != "SendEnqueuedMailMessageAsync");
+
+ foreach (var sendMethod in sendMethods)
+ {
+ await InvokeMethod(sendMethod);
+ }
+
+ async Task InvokeMethod(MethodInfo method)
+ {
+ var parameters = method.GetParameters();
+ var args = new object[parameters.Length];
+
+ for(var i = 0; i < parameters.Length; i++)
+ {
+ if (!namedParameters.TryGetValue((parameters[i].Name, parameters[i].ParameterType), out var value))
+ {
+ throw new InvalidOperationException($"Couldn't find a parameter for name '{parameters[i].Name}' and type '{parameters[i].ParameterType.FullName}'");
+ }
+
+ args[i] = value;
+ }
+
+ await (Task)method.Invoke(handlebarsService, args);
+ }
+ }
+
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]