aboutsummaryrefslogtreecommitdiff
path: root/Emails.Transactional.Client
diff options
context:
space:
mode:
Diffstat (limited to 'Emails.Transactional.Client')
-rw-r--r--Emails.Transactional.Client/ClientExtensions.cs55
-rw-r--r--Emails.Transactional.Client/EmailTransactionRequest.cs138
-rw-r--r--Emails.Transactional.Client/Emails.Transactional.Client.csproj33
-rw-r--r--Emails.Transactional.Client/Emails.Transactional.Client.xml308
-rw-r--r--Emails.Transactional.Client/Exceptions/InvalidAuthorizationException.cs26
-rw-r--r--Emails.Transactional.Client/Exceptions/InvalidTransactionRequestException.cs28
-rw-r--r--Emails.Transactional.Client/Exceptions/InvalidTransactionResponseException.cs40
-rw-r--r--Emails.Transactional.Client/Exceptions/TransactionExceptionBase.cs35
-rw-r--r--Emails.Transactional.Client/Exceptions/ValidationFailedException.cs62
-rw-r--r--Emails.Transactional.Client/TransactionResult.cs27
-rw-r--r--Emails.Transactional.Client/TransactionalEmailConfig.cs62
-rw-r--r--Emails.Transactional.Client/ValidationErrorMessage.cs21
12 files changed, 835 insertions, 0 deletions
diff --git a/Emails.Transactional.Client/ClientExtensions.cs b/Emails.Transactional.Client/ClientExtensions.cs
new file mode 100644
index 0000000..3246b9f
--- /dev/null
+++ b/Emails.Transactional.Client/ClientExtensions.cs
@@ -0,0 +1,55 @@
+using System;
+
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Emails.Transactional.Client.Exceptions;
+
+using RestSharp;
+
+namespace Emails.Transactional.Client
+{
+ /// <summary>
+ /// Contains extension methods to send emails on remote transational email servers
+ /// </summary>
+ public static class ClientExtensions
+ {
+ private const Method DEFAULT_SEND_METHOD = Method.Post;
+
+ /// <summary>
+ /// Asynchronously begins an email transaction against the mail server with the specified
+ /// transaction request.
+ /// </summary>
+ /// <param name="client"></param>
+ /// <param name="transaction">The <see cref="EmailTransactionRequest"/> to submit</param>
+ /// <param name="token">A cancelaion token to cancel the operation</param>
+ /// <returns>A task that represents the async send operation</returns>
+ /// <exception cref="ValidationFailedException"></exception>
+ /// <exception cref="InvalidAuthorizationException"></exception>
+ /// <exception cref="InvalidTransactionRequestException"></exception>
+ /// <exception cref="InvalidTransactionResponseException"></exception>
+ public static async Task<TransactionResult> SendEmailAsync(this RestClient client, EmailTransactionRequest transaction, CancellationToken token = default)
+ {
+ //Init the new request
+ RestRequest request = new(transaction.Endpoint, DEFAULT_SEND_METHOD)
+ {
+ //Json request
+ RequestFormat = DataFormat.Json
+ };
+ //add/serialze the transacion request
+ request.AddJsonBody(transaction);
+ //Exec the tranasction on the client
+ RestResponse<TransactionResult> response = await client.ExecuteAsync<TransactionResult>(request, token);
+ //parse the response body
+ return response.StatusCode switch
+ {
+ HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden => throw new InvalidAuthorizationException("The server did not accept the current authorization", response),
+ HttpStatusCode.BadRequest => throw new InvalidTransactionRequestException(response),
+ HttpStatusCode.UnprocessableEntity => throw new ValidationFailedException(response.Data),
+ HttpStatusCode.OK => response.Data,
+ _ => throw new InvalidTransactionResponseException("Unhandled status code", response),
+ };
+ }
+ }
+}
diff --git a/Emails.Transactional.Client/EmailTransactionRequest.cs b/Emails.Transactional.Client/EmailTransactionRequest.cs
new file mode 100644
index 0000000..1f1a4e2
--- /dev/null
+++ b/Emails.Transactional.Client/EmailTransactionRequest.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emails.Transactional.Client
+{
+ /// <summary>
+ /// A transactional email request to send an email
+ /// template.
+ /// </summary>
+ public class EmailTransactionRequest
+ {
+ /// <summary>
+ /// The transactional send endpoint address
+ /// </summary>
+ [JsonIgnore]
+ public Uri Endpoint { get; init; }
+ /// <summary>
+ /// A dictionary of email addresses/names of
+ /// users to send this email to
+ /// </summary>
+ [JsonPropertyName("to")]
+ public Dictionary<string, string> ToAddresses { get; set; }
+ /// <summary>
+ /// A dictionary of email addresses/names of
+ /// users to carbon copy this email to
+ /// </summary>
+ [JsonPropertyName("cc")]
+ public Dictionary<string, string> CcAddresses { get; set; }
+ /// <summary>
+ /// A dictionary of email addresses/names of
+ /// users to blind carbon copy this email to
+ /// </summary>
+ [JsonPropertyName("bcc")]
+ public Dictionary<string, string> BccAddresses { get; set; }
+ /// <summary>
+ /// A dictionary of variables to substitute into the liquid
+ /// email template
+ /// </summary>
+ [JsonPropertyName("variables")]
+ public Dictionary<string, string> Variables { get; set; }
+
+ /// <summary>
+ /// The subject of the email to send
+ /// </summary>
+ [JsonPropertyName("subject")]
+ public string Subject { get; set; }
+
+ /// <summary>
+ /// The unique id of the email template to send
+ /// </summary>
+ [JsonPropertyName("template_id")]
+ public string TemplateId { get; set; }
+ /// <summary>
+ /// The system from email name. NOTE: This is a protected value
+ /// </summary>
+ [JsonPropertyName("from_name")]
+ public string FromName { get; set; }
+ /// <summary>
+ /// The system from email address. NOTE: This is a protected value
+ /// </summary>
+ [JsonPropertyName("from_address")]
+ public string FromAddress { get; set; }
+
+ /// <summary>
+ /// Creates a new email transaction with the specified email template to send
+ /// </summary>
+ /// <param name="templateId">The id of the template to send</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public EmailTransactionRequest(string templateId)
+ {
+ this.TemplateId = templateId ?? throw new ArgumentNullException(nameof(templateId));
+ }
+ /// <summary>
+ /// Creates a new email transaction with the specified email template
+ /// and a single recipient
+ /// </summary>
+ /// <param name="templateId">The id of the template to send</param>
+ /// <param name="toAddress">A singular recipient name</param>
+ /// <param name="toName">A singlular recipient email address</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public EmailTransactionRequest(string templateId, string toName, string toAddress)
+ {
+ this.TemplateId = templateId ?? throw new ArgumentNullException(nameof(templateId));
+ AddToAddress(toName, toAddress);
+ }
+ /// <summary>
+ /// Adds a recipient to the To email address dictionary
+ /// </summary>
+ /// <param name="toName">The name of the user to send the email to</param>
+ /// <param name="toAddress">The unique email address of the user to add to the recipient collection</param>
+ public void AddToAddress(string toName, string toAddress)
+ {
+ ToAddresses ??= new(1);
+ ToAddresses.Add(toAddress, toName);
+ }
+ /// <summary>
+ /// Adds a recipient to the To email address dictionary
+ /// </summary>
+ /// <param name="toAddress">The unique email address of the user to add to the recipient collection</param>
+ public void AddToAddress(string toAddress)
+ {
+ string name = toAddress.Split('@')[0];
+ AddToAddress(name, toAddress);
+ }
+ /// <summary>
+ /// Adds a carbon copy recipient to the current cc dictionary
+ /// </summary>
+ /// <param name="ccName">The name of the recipient</param>
+ /// <param name="ccAddress">The unique email address of the bcc recipient</param>
+ public void AddCcAddress(string ccName, string ccAddress)
+ {
+ CcAddresses ??= new(1);
+ CcAddresses.Add(ccAddress, ccName);
+ }
+ /// <summary>
+ /// Adds a blind carbon copy recipient to the current bcc dictionary
+ /// </summary>
+ /// <param name="bccName">The name of the recipient</param>
+ /// <param name="bccAddress">The unique email address of the bcc recipient</param>
+ public void AddBccAddress(string bccName, string bccAddress)
+ {
+ BccAddresses ??= new(1);
+ BccAddresses.Add(bccAddress, bccName);
+ }
+ /// <summary>
+ /// Adds a liquid template variable to be subsituted by the template
+ /// renderer.
+ /// </summary>
+ /// <param name="varName">The unique name of the variable to add to the collection</param>
+ /// <param name="varValue">The value if the variable that will be substituted into the template</param>
+ public void AddVariable(string varName, string varValue)
+ {
+ Variables ??= new(1);
+ Variables.Add(varName, varValue);
+ }
+ }
+}
diff --git a/Emails.Transactional.Client/Emails.Transactional.Client.csproj b/Emails.Transactional.Client/Emails.Transactional.Client.csproj
new file mode 100644
index 0000000..e39cb07
--- /dev/null
+++ b/Emails.Transactional.Client/Emails.Transactional.Client.csproj
@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <Authors>Vaughn Nugent</Authors>
+ <Version>1.0.0.1</Version>
+ <Description>A client library for using the Emails.Transactional server plugin</Description>
+ <PackageProjectUrl>www.vaughnnugent.com/resources</PackageProjectUrl>
+ <Copyright>Copyright © 2022 Vaughn Nugent</Copyright>
+ <Platforms>AnyCPU;x64</Platforms>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+ <DocumentationFile></DocumentationFile>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <DocumentationFile></DocumentationFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="RestSharp" Version="108.0.2" />
+ </ItemGroup>
+
+</Project>
diff --git a/Emails.Transactional.Client/Emails.Transactional.Client.xml b/Emails.Transactional.Client/Emails.Transactional.Client.xml
new file mode 100644
index 0000000..013b85e
--- /dev/null
+++ b/Emails.Transactional.Client/Emails.Transactional.Client.xml
@@ -0,0 +1,308 @@
+<?xml version="1.0"?>
+<doc>
+ <assembly>
+ <name>Emails.Transactional.Client</name>
+ </assembly>
+ <members>
+ <member name="T:Emails.Transactional.Client.ClientExtensions">
+ <summary>
+ Contains extension methods to send emails on remote transational email servers
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.ClientExtensions.SendEmailAsync(RestSharp.RestClient,Emails.Transactional.Client.EmailTransactionRequest,System.Threading.CancellationToken)">
+ <summary>
+ Asynchronously begins an email transaction against the mail server with the specified
+ transaction request.
+ </summary>
+ <param name="client"></param>
+ <param name="transaction">The <see cref="T:Emails.Transactional.Client.EmailTransactionRequest"/> to submit</param>
+ <param name="token">A cancelaion token to cancel the operation</param>
+ <returns>A task that represents the async send operation</returns>
+ <exception cref="T:Emails.Transactional.Client.Exceptions.ValidationFailedException"></exception>
+ <exception cref="T:Emails.Transactional.Client.Exceptions.InvalidAuthorizationException"></exception>
+ <exception cref="T:Emails.Transactional.Client.Exceptions.InvalidTransactionRequestException"></exception>
+ <exception cref="T:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException"></exception>
+ </member>
+ <member name="T:Emails.Transactional.Client.EmailTransactionRequest">
+ <summary>
+ A transactional email request to send an email
+ template.
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.Endpoint">
+ <summary>
+ The transactional send endpoint address
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.ToAddresses">
+ <summary>
+ A dictionary of email addresses/names of
+ users to send this email to
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.CcAddresses">
+ <summary>
+ A dictionary of email addresses/names of
+ users to carbon copy this email to
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.BccAddresses">
+ <summary>
+ A dictionary of email addresses/names of
+ users to blind carbon copy this email to
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.Variables">
+ <summary>
+ A dictionary of variables to substitute into the liquid
+ email template
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.Subject">
+ <summary>
+ The subject of the email to send
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.TemplateId">
+ <summary>
+ The unique id of the email template to send
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.FromName">
+ <summary>
+ The system from email name. NOTE: This is a protected value
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.EmailTransactionRequest.FromAddress">
+ <summary>
+ The system from email address. NOTE: This is a protected value
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.#ctor(System.String)">
+ <summary>
+ Creates a new email transaction with the specified email template to send
+ </summary>
+ <param name="templateId">The id of the template to send</param>
+ <exception cref="T:System.ArgumentNullException"></exception>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.#ctor(System.String,System.String,System.String)">
+ <summary>
+ Creates a new email transaction with the specified email template
+ and a single recipient
+ </summary>
+ <param name="templateId">The id of the template to send</param>
+ <param name="toAddress">A singular recipient name</param>
+ <param name="toName">A singlular recipient email address</param>
+ <exception cref="T:System.ArgumentNullException"></exception>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.AddToAddress(System.String,System.String)">
+ <summary>
+ Adds a recipient to the To email address dictionary
+ </summary>
+ <param name="toName">The name of the user to send the email to</param>
+ <param name="toAddress">The unique email address of the user to add to the recipient collection</param>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.AddToAddress(System.String)">
+ <summary>
+ Adds a recipient to the To email address dictionary
+ </summary>
+ <param name="toAddress">The unique email address of the user to add to the recipient collection</param>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.AddCcAddress(System.String,System.String)">
+ <summary>
+ Adds a carbon copy recipient to the current cc dictionary
+ </summary>
+ <param name="ccName">The name of the recipient</param>
+ <param name="ccAddress">The unique email address of the bcc recipient</param>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.AddBccAddress(System.String,System.String)">
+ <summary>
+ Adds a blind carbon copy recipient to the current bcc dictionary
+ </summary>
+ <param name="bccName">The name of the recipient</param>
+ <param name="bccAddress">The unique email address of the bcc recipient</param>
+ </member>
+ <member name="M:Emails.Transactional.Client.EmailTransactionRequest.AddVariable(System.String,System.String)">
+ <summary>
+ Adds a liquid template variable to be subsituted by the template
+ renderer.
+ </summary>
+ <param name="varName">The unique name of the variable to add to the collection</param>
+ <param name="varValue">The value if the variable that will be substituted into the template</param>
+ </member>
+ <member name="T:Emails.Transactional.Client.Exceptions.InvalidAuthorizationException">
+ <summary>
+ A excption raised when an Authorization error occured
+ during a request
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidAuthorizationException.#ctor">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidAuthorizationException.#ctor(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidAuthorizationException.#ctor(System.String,System.Exception)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidAuthorizationException.#ctor(System.String,RestSharp.RestResponse{Emails.Transactional.Client.TransactionResult})">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionRequestException.#ctor">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionRequestException.#ctor(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionRequestException.#ctor(System.String,System.Exception)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionRequestException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
+ <inheritdoc/>
+ </member>
+ <member name="T:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException">
+ <summary>
+ Raised when the results of an email transaction
+ failed. Inner exceptions may be set
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException.ErrorMessage">
+ <summary>
+ An error message received from the client
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException.#ctor">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException.#ctor(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException.#ctor(System.String,System.Exception)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException.#ctor(System.String,RestSharp.RestResponse{Emails.Transactional.Client.TransactionResult})">
+ <summary>
+ Initializes a new <see cref="T:Emails.Transactional.Client.Exceptions.InvalidTransactionResponseException"/> with
+ the response that contains the error
+ </summary>
+ <param name="message">The base exception message</param>
+ <param name="response">The response that caused the error</param>
+ </member>
+ <member name="T:Emails.Transactional.Client.Exceptions.TransactionExceptionBase">
+ <summary>
+ A base exception for all client transaction excepions
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.TransactionExceptionBase.#ctor">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.TransactionExceptionBase.#ctor(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.TransactionExceptionBase.#ctor(System.String,System.Exception)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.TransactionExceptionBase.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
+ <inheritdoc/>
+ </member>
+ <member name="P:Emails.Transactional.Client.Exceptions.TransactionExceptionBase.ErrorResponse">
+ <summary>
+ The response objec that caused the exception
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.Exceptions.TransactionExceptionBase.ResultMessage">
+ <summary>
+ The string represenation of the response body
+ </summary>
+ </member>
+ <member name="T:Emails.Transactional.Client.Exceptions.ValidationFailedException">
+ <summary>
+ Raised when server message validation failed
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.ValidationFailedException.#ctor">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.ValidationFailedException.#ctor(System.String)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.ValidationFailedException.#ctor(System.String,System.Exception)">
+ <inheritdoc/>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.ValidationFailedException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
+ <inheritdoc/>
+ </member>
+ <member name="P:Emails.Transactional.Client.Exceptions.ValidationFailedException.ValidationErrors">
+ <summary>
+ A collection of validaion error messages
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.Exceptions.ValidationFailedException.#ctor(Emails.Transactional.Client.TransactionResult)">
+ <summary>
+
+ </summary>
+ <param name="result"></param>
+ </member>
+ <member name="T:Emails.Transactional.Client.TransactionalEmailConfig">
+ <summary>
+ A global configuration object for transactional email clients
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.TransactionalEmailConfig.ServiceLocation">
+ <summary>
+ The server transaction endpoint location
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.TransactionalEmailConfig.TemplateIdLookup">
+ <summary>
+ An email id template/translation table for email template-ids
+ </summary>
+ </member>
+ <member name="M:Emails.Transactional.Client.TransactionalEmailConfig.WithUrl(System.Uri)">
+ <summary>
+ Adds the mail service location to the current instance
+ </summary>
+ <param name="serviceLocation">The address of the remote server transaction endpoint</param>
+ <returns>A referrence to the current object (fluent api)</returns>
+ <exception cref="T:System.ArgumentNullException"></exception>
+ </member>
+ <member name="M:Emails.Transactional.Client.TransactionalEmailConfig.WithTemplates(System.Collections.Generic.IReadOnlyDictionary{System.String,System.String})">
+ <summary>
+ Sets the template lookup table for the current instance
+ </summary>
+ <param name="templates">The template-id lookup table to referrence</param>
+ <returns>A referrence to the current object (fluent api)</returns>
+ <exception cref="T:System.ArgumentNullException"></exception>
+ </member>
+ <member name="M:Emails.Transactional.Client.TransactionalEmailConfig.GetTemplateRequest(System.String)">
+ <summary>
+ Gets a new <see cref="T:Emails.Transactional.Client.EmailTransactionRequest"/> from the specifed
+ template name.
+ </summary>
+ <param name="templateName"></param>
+ <returns></returns>
+ <exception cref="T:System.Collections.Generic.KeyNotFoundException"></exception>
+ <exception cref="T:System.ArgumentNullException"></exception>
+ </member>
+ <member name="T:Emails.Transactional.Client.TransactionResult">
+ <summary>
+ A JSON serializable object that contains the results of the transaction
+ </summary>
+ </member>
+ <member name="T:Emails.Transactional.Client.ValidationErrorMessage">
+ <summary>
+ A json serializable server validaion error
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.ValidationErrorMessage.PropertyName">
+ <summary>
+ The name of the propery that was invalid
+ </summary>
+ </member>
+ <member name="P:Emails.Transactional.Client.ValidationErrorMessage.ErrorMessage">
+ <summary>
+ The message that
+ </summary>
+ </member>
+ </members>
+</doc>
diff --git a/Emails.Transactional.Client/Exceptions/InvalidAuthorizationException.cs b/Emails.Transactional.Client/Exceptions/InvalidAuthorizationException.cs
new file mode 100644
index 0000000..eed2f42
--- /dev/null
+++ b/Emails.Transactional.Client/Exceptions/InvalidAuthorizationException.cs
@@ -0,0 +1,26 @@
+using System;
+
+using RestSharp;
+
+namespace Emails.Transactional.Client.Exceptions
+{
+ /// <summary>
+ /// A excption raised when an Authorization error occured
+ /// during a request
+ /// </summary>
+ public class InvalidAuthorizationException : InvalidTransactionResponseException
+ {
+ ///<inheritdoc/>
+ public InvalidAuthorizationException():base()
+ {}
+ ///<inheritdoc/>
+ public InvalidAuthorizationException(string message) : base(message)
+ {}
+ ///<inheritdoc/>
+ public InvalidAuthorizationException(string message, Exception innerException) : base(message, innerException)
+ {}
+ ///<inheritdoc/>
+ public InvalidAuthorizationException(string message, RestResponse<TransactionResult> response) : base(message, response)
+ {}
+ }
+}
diff --git a/Emails.Transactional.Client/Exceptions/InvalidTransactionRequestException.cs b/Emails.Transactional.Client/Exceptions/InvalidTransactionRequestException.cs
new file mode 100644
index 0000000..11d4bd9
--- /dev/null
+++ b/Emails.Transactional.Client/Exceptions/InvalidTransactionRequestException.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.Serialization;
+
+using RestSharp;
+
+namespace Emails.Transactional.Client.Exceptions
+{
+ public class InvalidTransactionRequestException : TransactionExceptionBase
+ {
+ ///<inheritdoc/>
+ public InvalidTransactionRequestException()
+ {}
+ ///<inheritdoc/>
+ public InvalidTransactionRequestException(string message) : base(message)
+ {}
+ ///<inheritdoc/>
+ public InvalidTransactionRequestException(string message, Exception innerException) : base(message, innerException)
+ {}
+ ///<inheritdoc/>
+ protected InvalidTransactionRequestException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {}
+
+ public InvalidTransactionRequestException(RestResponse response)
+ {
+ this.ErrorResponse = response;
+ }
+ }
+}
diff --git a/Emails.Transactional.Client/Exceptions/InvalidTransactionResponseException.cs b/Emails.Transactional.Client/Exceptions/InvalidTransactionResponseException.cs
new file mode 100644
index 0000000..ed64a0c
--- /dev/null
+++ b/Emails.Transactional.Client/Exceptions/InvalidTransactionResponseException.cs
@@ -0,0 +1,40 @@
+using System;
+using RestSharp;
+
+namespace Emails.Transactional.Client.Exceptions
+{
+ /// <summary>
+ /// Raised when the results of an email transaction
+ /// failed. Inner exceptions may be set
+ /// </summary>
+ public class InvalidTransactionResponseException : TransactionExceptionBase
+ {
+ /// <summary>
+ /// An error message received from the client
+ /// </summary>
+ public TransactionResult ErrorMessage { get; init; }
+
+ ///<inheritdoc/>
+ public InvalidTransactionResponseException()
+ {}
+ ///<inheritdoc/>
+ public InvalidTransactionResponseException(string message) : base(message)
+ {}
+ ///<inheritdoc/>
+ public InvalidTransactionResponseException(string message, Exception innerException) : base(message, innerException)
+ {}
+
+ /// <summary>
+ /// Initializes a new <see cref="InvalidTransactionResponseException"/> with
+ /// the response that contains the error
+ /// </summary>
+ /// <param name="message">The base exception message</param>
+ /// <param name="response">The response that caused the error</param>
+ public InvalidTransactionResponseException(string message, RestResponse<TransactionResult> response) : base(message, response.ErrorException)
+ {
+ this.ErrorResponse = response;
+ //See if the server sent an error message
+ this.ErrorMessage = response.Data;
+ }
+ }
+}
diff --git a/Emails.Transactional.Client/Exceptions/TransactionExceptionBase.cs b/Emails.Transactional.Client/Exceptions/TransactionExceptionBase.cs
new file mode 100644
index 0000000..6cee30d
--- /dev/null
+++ b/Emails.Transactional.Client/Exceptions/TransactionExceptionBase.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Runtime.Serialization;
+
+using RestSharp;
+
+namespace Emails.Transactional.Client.Exceptions
+{
+ /// <summary>
+ /// A base exception for all client transaction excepions
+ /// </summary>
+ public class TransactionExceptionBase : Exception
+ {
+ ///<inheritdoc/>
+ public TransactionExceptionBase()
+ {}
+ ///<inheritdoc/>
+ public TransactionExceptionBase(string message) : base(message)
+ {}
+ ///<inheritdoc/>
+ public TransactionExceptionBase(string message, Exception innerException) : base(message, innerException)
+ {}
+ ///<inheritdoc/>
+ protected TransactionExceptionBase(SerializationInfo info, StreamingContext context) : base(info, context)
+ {}
+
+ /// <summary>
+ /// The response objec that caused the exception
+ /// </summary>
+ public RestResponse ErrorResponse { get; init; }
+ /// <summary>
+ /// The string represenation of the response body
+ /// </summary>
+ public string ResultMessage => ErrorResponse.Content;
+ }
+} \ No newline at end of file
diff --git a/Emails.Transactional.Client/Exceptions/ValidationFailedException.cs b/Emails.Transactional.Client/Exceptions/ValidationFailedException.cs
new file mode 100644
index 0000000..12bcc33
--- /dev/null
+++ b/Emails.Transactional.Client/Exceptions/ValidationFailedException.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+
+#nullable enable
+
+namespace Emails.Transactional.Client.Exceptions
+{
+ /// <summary>
+ /// Raised when server message validation failed
+ /// </summary>
+ public class ValidationFailedException : InvalidTransactionRequestException
+ {
+ ///<inheritdoc/>
+ public ValidationFailedException()
+ {}
+ ///<inheritdoc/>
+ public ValidationFailedException(string message) : base(message)
+ {}
+ ///<inheritdoc/>
+ public ValidationFailedException(string message, Exception innerException) : base(message, innerException)
+ {}
+
+ /// <summary>
+ /// A collection of validaion error messages
+ /// </summary>
+ public ICollection<ValidationErrorMessage>? ValidationErrors { get; init; }
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="result"></param>
+ public ValidationFailedException(TransactionResult result):base("Transaction data server validation failed")
+ {
+ this.ValidationErrors = result?.ValidationErrors;
+ }
+
+ ///<inheritdoc/>
+ public override string Message
+ {
+ get
+ {
+ if(ValidationErrors == null)
+ {
+ return base.Message;
+
+ }
+ StringBuilder sb = new(base.Message);
+ sb.AppendLine();
+
+ foreach(var kvp in ValidationErrors)
+ {
+ sb.Append("Validation error: ");
+ sb.Append(kvp.PropertyName);
+ sb.Append(' ');
+ sb.AppendLine(kvp.ErrorMessage);
+ }
+ return sb.ToString();
+ }
+ }
+ }
+}
diff --git a/Emails.Transactional.Client/TransactionResult.cs b/Emails.Transactional.Client/TransactionResult.cs
new file mode 100644
index 0000000..5c97b3d
--- /dev/null
+++ b/Emails.Transactional.Client/TransactionResult.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emails.Transactional.Client
+{
+ /// <summary>
+ /// A JSON serializable object that contains the results of the transaction
+ /// </summary>
+ public class TransactionResult
+ {
+ [JsonPropertyName("transaction_id")]
+ public string TransactionId { get; set; }
+ [JsonPropertyName("smtp_status")]
+ public string SmtpStatus { get; set; }
+ [JsonPropertyName("success")]
+ public bool Success { get; set; }
+
+ [JsonPropertyName("error_code")]
+ public string ErrorCode { get; set; }
+
+ [JsonPropertyName("error_description")]
+ public string ErrorDescription { get; set; }
+
+ [JsonPropertyName("errors")]
+ public ICollection<ValidationErrorMessage> ValidationErrors { get; set; }
+ }
+}
diff --git a/Emails.Transactional.Client/TransactionalEmailConfig.cs b/Emails.Transactional.Client/TransactionalEmailConfig.cs
new file mode 100644
index 0000000..9efeba4
--- /dev/null
+++ b/Emails.Transactional.Client/TransactionalEmailConfig.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+
+namespace Emails.Transactional.Client
+{
+ /// <summary>
+ /// A global configuration object for transactional email clients
+ /// </summary>
+ public class TransactionalEmailConfig
+ {
+ /// <summary>
+ /// The server transaction endpoint location
+ /// </summary>
+ public Uri ServiceLocation { get; private set; }
+
+ /// <summary>
+ /// An email id template/translation table for email template-ids
+ /// </summary>
+ public IReadOnlyDictionary<string, string> TemplateIdLookup { get; private set; }
+
+ /// <summary>
+ /// Adds the mail service location to the current instance
+ /// </summary>
+ /// <param name="serviceLocation">The address of the remote server transaction endpoint</param>
+ /// <returns>A referrence to the current object (fluent api)</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public TransactionalEmailConfig WithUrl(Uri serviceLocation)
+ {
+ ServiceLocation = serviceLocation ?? throw new ArgumentNullException(nameof(serviceLocation));
+ return this;
+ }
+ /// <summary>
+ /// Sets the template lookup table for the current instance
+ /// </summary>
+ /// <param name="templates">The template-id lookup table to referrence</param>
+ /// <returns>A referrence to the current object (fluent api)</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public TransactionalEmailConfig WithTemplates(IReadOnlyDictionary<string, string> templates)
+ {
+ TemplateIdLookup = templates ?? throw new ArgumentNullException(nameof(templates));
+ return this;
+ }
+
+ /// <summary>
+ /// Gets a new <see cref="EmailTransactionRequest"/> from the specifed
+ /// template name.
+ /// </summary>
+ /// <param name="templateName"></param>
+ /// <returns></returns>
+ /// <exception cref="KeyNotFoundException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ public EmailTransactionRequest GetTemplateRequest(string templateName)
+ {
+ //get the template from its template name
+ string templateId = TemplateIdLookup[templateName];
+ return new EmailTransactionRequest(templateId)
+ {
+ Endpoint = this.ServiceLocation
+ };
+ }
+ }
+}
diff --git a/Emails.Transactional.Client/ValidationErrorMessage.cs b/Emails.Transactional.Client/ValidationErrorMessage.cs
new file mode 100644
index 0000000..5e7d728
--- /dev/null
+++ b/Emails.Transactional.Client/ValidationErrorMessage.cs
@@ -0,0 +1,21 @@
+using System.Text.Json.Serialization;
+
+namespace Emails.Transactional.Client
+{
+ /// <summary>
+ /// A json serializable server validaion error
+ /// </summary>
+ public class ValidationErrorMessage
+ {
+ /// <summary>
+ /// The name of the propery that was invalid
+ /// </summary>
+ [JsonPropertyName("property")]
+ public string PropertyName { get; set; }
+ /// <summary>
+ /// The message that
+ /// </summary>
+ [JsonPropertyName("message")]
+ public string ErrorMessage { get; set; }
+ }
+}