aboutsummaryrefslogtreecommitdiff
path: root/Transactional Emails/Transactions
diff options
context:
space:
mode:
Diffstat (limited to 'Transactional Emails/Transactions')
-rw-r--r--Transactional Emails/Transactions/EmailTransactionValidator.cs109
-rw-r--r--Transactional Emails/Transactions/TransactionResult.cs20
-rw-r--r--Transactional Emails/Transactions/TransactionStore.cs50
3 files changed, 179 insertions, 0 deletions
diff --git a/Transactional Emails/Transactions/EmailTransactionValidator.cs b/Transactional Emails/Transactions/EmailTransactionValidator.cs
new file mode 100644
index 0000000..09571bf
--- /dev/null
+++ b/Transactional Emails/Transactions/EmailTransactionValidator.cs
@@ -0,0 +1,109 @@
+using FluentValidation;
+
+using VNLib.Plugins.Extensions.Validation;
+
+
+namespace Emails.Transactional.Transactions
+{
+ internal class EmailTransactionValidator : AbstractValidator<EmailTransaction>
+ {
+ public const int MAX_SUBJECT_LEN = 50;
+ public EmailTransactionValidator()
+ {
+ //Catch to make sure user-id is set
+ RuleFor(static t => t.UserId)
+ .NotEmpty();
+ //validate from addres/name
+ RuleFor(static t => t.FromName)
+ .NotEmpty()
+ .WithName("from_name")
+ .AlphaNumericOnly()
+ .WithName("from_name");
+ RuleFor(static t => t.From)
+ .NotEmpty()
+ .EmailAddress()
+ .WithName("from");
+
+ //Rule names must be their json propery name, so clients can properly log errors
+ //must include a template id
+ RuleFor(static t => t.TemplateId)
+ .NotEmpty()
+ .MaximumLength(200)
+ .WithName("template_id");
+
+ //From address must not be empty
+ RuleFor(static t => t.From)
+ .NotEmpty()
+ .EmailAddress();
+ //Subject is required alpha num
+ RuleFor(static t => t.Subject)
+ .NotEmpty()
+ .AlphaNumericOnly()
+ .MaximumLength(MAX_SUBJECT_LEN)
+ .WithName("subject");
+
+ //To address must not be empty, and must be valid email addresses
+ RuleFor(static t => t.ToAddresses)
+ .NotEmpty()
+ .ChildRules(static to => {
+ //Check keys (address)
+ to.RuleForEach(static b => b.Keys)
+ .NotEmpty()
+ .EmailAddress()
+ .WithName("to");
+
+ //Check values (names)
+ to.RuleForEach(static b => b.Values)
+ .NotEmpty()
+ .AlphaNumericOnly()
+ .WithName("to_name");
+ })
+ .WithName("to");
+ //Check bcc addresses, allowed to be empty
+ RuleFor(static t => t.BccAddresses)
+ .ChildRules(static bcc => {
+ //Check keys (address)
+ bcc.RuleForEach(static b => b.Keys)
+ .NotEmpty()
+ .EmailAddress()
+ .WithName("bcc");
+
+ //Check values (names)
+ bcc.RuleForEach(static b => b.Values)
+ .NotEmpty()
+ .AlphaNumericOnly()
+ .WithName("bcc_names");
+ })
+ .When(static t => t.BccAddresses != null)
+ .WithName("bcc");
+ //Check cc, also allowed to be empty
+ RuleFor(static t => t.CcAddresses)
+ .ChildRules(static cc => {
+ //Check keys (names)
+ cc.RuleForEach(static b => b.Keys)
+ .NotEmpty()
+ .EmailAddress()
+ .WithName("cc");
+
+ //Check values (addresses)
+ cc.RuleForEach(static b => b.Values)
+ .NotEmpty()
+ .AlphaNumericOnly()
+ .WithName("cc_names");
+ })
+ .When(static t => t.BccAddresses != null)
+ .WithName("cc");
+
+ RuleFor(static t => t.ReplyTo)
+ //Allow the reply to email to be empty, if its not, then validate the address
+ .EmailAddress()
+ .When(static t => t.BccAddresses != null)
+ .WithName("reply_to");
+
+ //Make sure a variable table is defined
+ RuleFor(static t => t.Variables)
+ .NotNull()
+ .WithName("variables");
+ }
+ }
+}
diff --git a/Transactional Emails/Transactions/TransactionResult.cs b/Transactional Emails/Transactions/TransactionResult.cs
new file mode 100644
index 0000000..2f14875
--- /dev/null
+++ b/Transactional Emails/Transactions/TransactionResult.cs
@@ -0,0 +1,20 @@
+using System.Text.Json.Serialization;
+
+using VNLib.Plugins.Extensions.Validation;
+
+namespace Emails.Transactional.Transactions
+{
+ public class TransactionResult : ValErrWebMessage
+ {
+ [JsonPropertyName("transaction_id")]
+ public string TransactionId { get; set; }
+ [JsonPropertyName("smtp_status")]
+ public string SmtpStatus { get; set; }
+
+ [JsonPropertyName("error_code")]
+ public string ErrorCode { get; set; }
+
+ [JsonPropertyName("error_description")]
+ public string ErrorDescription { get; set; }
+ }
+}
diff --git a/Transactional Emails/Transactions/TransactionStore.cs b/Transactional Emails/Transactions/TransactionStore.cs
new file mode 100644
index 0000000..9a700ae
--- /dev/null
+++ b/Transactional Emails/Transactions/TransactionStore.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Linq;
+
+using Microsoft.EntityFrameworkCore;
+
+using VNLib.Plugins.Extensions.Data;
+
+namespace Emails.Transactional.Transactions
+{
+
+ internal class TransactionStore : DbStore<EmailTransaction>
+ {
+ private readonly DbContextOptions Options;
+
+ public TransactionStore(DbContextOptions options)
+ {
+ Options = options;
+ }
+
+ public override TransactionalDbContext NewContext() => new EmailDbCtx(Options);
+
+ public override string RecordIdBuilder => Guid.NewGuid().ToString("N");
+
+ protected override void OnRecordUpdate(EmailTransaction newRecord, EmailTransaction oldRecord)
+ {
+ oldRecord.LastModified = DateTime.UtcNow;
+ }
+
+ protected override IQueryable<EmailTransaction> GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints)
+ {
+ string userId = constraints[0];
+ //Get the last transactions for the specifed user
+ EmailDbCtx ctx = context as EmailDbCtx;
+ return from trans in ctx.EmailTransactions
+ where trans.UserId == userId
+ orderby trans.LastModified descending
+ select trans;
+ }
+
+ protected override IQueryable<EmailTransaction> GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints)
+ {
+ string transactionid = constraints[0];
+ EmailDbCtx ctx = context as EmailDbCtx;
+ //Selet the exact transaction from its id
+ return from trans in ctx.EmailTransactions
+ where trans.Id == transactionid
+ select trans;
+ }
+ }
+}