diff options
Diffstat (limited to 'lib/Emails.Transactional.Client/src')
12 files changed, 1086 insertions, 0 deletions
diff --git a/lib/Emails.Transactional.Client/src/ClientExtensions.cs b/lib/Emails.Transactional.Client/src/ClientExtensions.cs new file mode 100644 index 0000000..5c6fb12 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/ClientExtensions.cs @@ -0,0 +1,79 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: ClientExtensions.cs +* +* ClientExtensions.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/EmailTransactionRequest.cs b/lib/Emails.Transactional.Client/src/EmailTransactionRequest.cs new file mode 100644 index 0000000..d6b1ebe --- /dev/null +++ b/lib/Emails.Transactional.Client/src/EmailTransactionRequest.cs @@ -0,0 +1,162 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: EmailTransactionRequest.cs +* +* EmailTransactionRequest.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/Emails.Transactional.Client.csproj b/lib/Emails.Transactional.Client/src/Emails.Transactional.Client.csproj new file mode 100644 index 0000000..783c3b9 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Emails.Transactional.Client.csproj @@ -0,0 +1,41 @@ +<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> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + </PropertyGroup> + + <PropertyGroup> + <DocumentationFile></DocumentationFile> + <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> + <Nullable>enable</Nullable> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + </PropertyGroup> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> + <Deterministic>False</Deterministic> + </PropertyGroup> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> + <Deterministic>False</Deterministic> + </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.3" /> + </ItemGroup> + +</Project> diff --git a/lib/Emails.Transactional.Client/src/Emails.Transactional.Client.xml b/lib/Emails.Transactional.Client/src/Emails.Transactional.Client.xml new file mode 100644 index 0000000..a69734e --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Emails.Transactional.Client.xml @@ -0,0 +1,311 @@ +<?xml version="1.0"?> +<!-- +Copyright (c) 2022 Vaughn Nugent +--> +<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/lib/Emails.Transactional.Client/src/Exceptions/InvalidAuthorizationException.cs b/lib/Emails.Transactional.Client/src/Exceptions/InvalidAuthorizationException.cs new file mode 100644 index 0000000..2f31ad9 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Exceptions/InvalidAuthorizationException.cs @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: InvalidAuthorizationException.cs +* +* InvalidAuthorizationException.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/Exceptions/InvalidTransactionRequestException.cs b/lib/Emails.Transactional.Client/src/Exceptions/InvalidTransactionRequestException.cs new file mode 100644 index 0000000..a841d92 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Exceptions/InvalidTransactionRequestException.cs @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: InvalidTransactionRequestException.cs +* +* InvalidTransactionRequestException.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/Exceptions/InvalidTransactionResponseException.cs b/lib/Emails.Transactional.Client/src/Exceptions/InvalidTransactionResponseException.cs new file mode 100644 index 0000000..100e351 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Exceptions/InvalidTransactionResponseException.cs @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: InvalidTransactionResponseException.cs +* +* InvalidTransactionResponseException.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/Exceptions/TransactionExceptionBase.cs b/lib/Emails.Transactional.Client/src/Exceptions/TransactionExceptionBase.cs new file mode 100644 index 0000000..a442470 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Exceptions/TransactionExceptionBase.cs @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: TransactionExceptionBase.cs +* +* TransactionExceptionBase.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/Exceptions/ValidationFailedException.cs b/lib/Emails.Transactional.Client/src/Exceptions/ValidationFailedException.cs new file mode 100644 index 0000000..d8f4bdf --- /dev/null +++ b/lib/Emails.Transactional.Client/src/Exceptions/ValidationFailedException.cs @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: ValidationFailedException.cs +* +* ValidationFailedException.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/TransactionResult.cs b/lib/Emails.Transactional.Client/src/TransactionResult.cs new file mode 100644 index 0000000..279e0d2 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/TransactionResult.cs @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: TransactionResult.cs +* +* TransactionResult.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/TransactionalEmailConfig.cs b/lib/Emails.Transactional.Client/src/TransactionalEmailConfig.cs new file mode 100644 index 0000000..1ce5459 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/TransactionalEmailConfig.cs @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: TransactionalEmailConfig.cs +* +* TransactionalEmailConfig.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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/lib/Emails.Transactional.Client/src/ValidationErrorMessage.cs b/lib/Emails.Transactional.Client/src/ValidationErrorMessage.cs new file mode 100644 index 0000000..e2e5f44 --- /dev/null +++ b/lib/Emails.Transactional.Client/src/ValidationErrorMessage.cs @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional.Client +* File: ValidationErrorMessage.cs +* +* ValidationErrorMessage.cs is part of Emails.Transactional.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* Emails.Transactional.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Emails.Transactional.Client. If not, see http://www.gnu.org/licenses/. +*/ + +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; } + } +} |