diff --git a/Example Projects/dotNetCoreExample/Examples/Basic/BasicComplexExample.cs b/Example Projects/dotNetCoreExample/Examples/Basic/BasicComplexExample.cs index f95d1c2..47846d3 100644 --- a/Example Projects/dotNetCoreExample/Examples/Basic/BasicComplexExample.cs +++ b/Example Projects/dotNetCoreExample/Examples/Basic/BasicComplexExample.cs @@ -48,6 +48,22 @@ public SendResponse RunExample() message.CustomHeaders.Add("x-mycustomheader", "I am a message header"); + var metadata = new List() + { + new Metadata("example-type", "basic-send-complex"), + new Metadata() + { + Key = "message-contains", + Value = "attachments, headers" + } + }; + message.Metadata.Add(metadata); + message.Metadata.Add("x-mycustommetadata", "I am custom metadata"); + message.Metadata.Add(new Metadata("testMessageHeader", "I am metadata")); + + message.Tags.Add("Basic-Complex-Example"); + message.Tags.Add("c#-Example"); + var attachment = message.Attachments.Add("bus.png", MimeType.PNG, @".\examples\img\bus.png"); attachment.ContentId = "Bus"; diff --git a/Example Projects/dotNetCoreExample/Examples/Bulk/BulkSendComplexExample.cs b/Example Projects/dotNetCoreExample/Examples/Bulk/BulkSendComplexExample.cs index 53553e4..06dfb12 100644 --- a/Example Projects/dotNetCoreExample/Examples/Bulk/BulkSendComplexExample.cs +++ b/Example Projects/dotNetCoreExample/Examples/Bulk/BulkSendComplexExample.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Collections.Generic; +using System.Text; using System.Text.RegularExpressions; using SocketLabs.InjectionApi; using SocketLabs.InjectionApi.Message; @@ -41,7 +42,23 @@ public SendResponse RunExample() message.From.Set("from@example.com", "FromMe"); message.ReplyTo.Email = "replyto@example.com"; - message.CustomHeaders.Add(new CustomHeader("testMessageHeader", "I am a message header")); + var metadata = new List() + { + new Metadata("example-type", "bulk-send-complex"), + new Metadata() + { + Key = "message-contains", + Value = "attachments, headers" + } + }; + message.Metadata.Add(metadata); + message.Metadata.Add("x-mycustommetadata", "I am custom metadata"); + message.Metadata.Add(new Metadata("testMessageHeader", "I am metadata")); + + message.Metadata.Add("x-mycustommetadata", "I am custom metadata"); + + message.Tags.Add("Bulk-Complex-Example"); + message.Tags.Add("c#-Example"); // Build the Content (Note the %% symbols used to denote the data to be merged) var html = new StringBuilder(); diff --git a/Example Projects/dotNetCoreExample/dotNetCoreExample.csproj b/Example Projects/dotNetCoreExample/dotNetCoreExample.csproj index cc4eba3..29f1a3d 100644 --- a/Example Projects/dotNetCoreExample/dotNetCoreExample.csproj +++ b/Example Projects/dotNetCoreExample/dotNetCoreExample.csproj @@ -3,7 +3,7 @@ Exe net6.0 - Copyright © 2018-2022 SocketLabs Acquisition LLC + Copyright © 2018-2023 SocketLabs Acquisition LLC diff --git a/README.md b/README.md index e34aa9b..0d4056a 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ PM> Install-Package SocketLabs.EmailDelivery Adding a Package Reference to your project: ``` - + ``` .NET CLI users can also use the following command: diff --git a/docs/release-notes/latest.md b/docs/release-notes/latest.md index d20274b..b75b7f6 100644 --- a/docs/release-notes/latest.md +++ b/docs/release-notes/latest.md @@ -1,3 +1,11 @@ ## 1.3.4 * Bump Castle.Core from 5.0.0 to 5.1.0 -* Bump Moq from 4.18.1 to 4.18.2 \ No newline at end of file +* Bump Moq from 4.18.1 to 4.18.2 + +## 1.4.0 +* Adding Metadata and Tags +* Bump coverlet.collector from 3.1.2 to 3.2.0 +* Bump Newtonsoft.Json from 13.0.1 to 13.0.2 +* Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.4.1 +* Bump MSTest.TestFramework from 2.2.10 to 3.0.2 +* Bump Moq from 4.18.2 to 4.18.4 diff --git a/src/SocketLabs/InjectionApi/Core/InjectionRequestFactory.cs b/src/SocketLabs/InjectionApi/Core/InjectionRequestFactory.cs index 1df7b56..2e38dbe 100644 --- a/src/SocketLabs/InjectionApi/Core/InjectionRequestFactory.cs +++ b/src/SocketLabs/InjectionApi/Core/InjectionRequestFactory.cs @@ -92,7 +92,9 @@ internal virtual MessageJson GenerateBaseMessageJson(IMessageBase message) CharSet = message.CharSet, CustomHeaders = PopulateCustomHeaders(message.CustomHeaders), From = new AddressJson(message.From.Email, message.From.FriendlyName), - Attachments = PopulateList(message.Attachments) + Attachments = PopulateList(message.Attachments), + Metadata = PopulateMetadata(message.Metadata), + Tags = PopulateTags(message.Tags) }; if (message.ReplyTo != null) @@ -192,5 +194,28 @@ internal virtual List PopulateMergeData(IDictionary new MergeFieldJson(item.Key, item.Value)); return result?.ToList(); } + + + /// + /// Converting a ]]> to a ]]> + /// + /// A ]]> from the message + /// A ]]> used in generating an InjectionRequest + internal virtual List PopulateMetadata(IList metadata) + { + var result = metadata?.Select(item => new MetadataHeaderJson(item.Key, item.Value)); + return result?.ToList(); + } + + /// + /// Converting a ]]> to a ]]> + /// + /// A ]]> from the message + /// A ]]> used in generating an InjectionRequest + internal virtual List PopulateTags(IList tags) + { + var result = tags.ToList(); + return result?.ToList(); + } } } diff --git a/src/SocketLabs/InjectionApi/Core/SendValidator.cs b/src/SocketLabs/InjectionApi/Core/SendValidator.cs index 9ae75f5..56a3427 100644 --- a/src/SocketLabs/InjectionApi/Core/SendValidator.cs +++ b/src/SocketLabs/InjectionApi/Core/SendValidator.cs @@ -78,6 +78,9 @@ internal virtual SendResult ValidateIMessageBase(IMessageBase message) if (message.CustomHeaders != null && message.CustomHeaders.Any()) if (!HasValidCustomHeaders(message.CustomHeaders)) return SendResult.MessageValidationInvalidCustomHeaders; + if (message.Metadata != null && message.Metadata.Any()) + if (!HasValidMetadata(message.Metadata)) return SendResult.MessageValidationInvalidMetadata; + return SendResult.Success; } @@ -334,5 +337,16 @@ internal virtual bool HasValidCustomHeaders(IList customHeaders) var result = customHeaders?.Where(item => !item.IsValid); return result == null || !result.Any(); } + + /// + /// Check if IMetadata in List are valid + /// + /// ]]> to validate + /// bool result + internal virtual bool HasValidMetadata(IList metadata) + { + var result = metadata?.Where(item => !item.IsValid); + return result == null || !result.Any(); + } } } diff --git a/src/SocketLabs/InjectionApi/Core/Serialization/MessageJson.cs b/src/SocketLabs/InjectionApi/Core/Serialization/MessageJson.cs index b0aa71e..dfe60e7 100644 --- a/src/SocketLabs/InjectionApi/Core/Serialization/MessageJson.cs +++ b/src/SocketLabs/InjectionApi/Core/Serialization/MessageJson.cs @@ -19,6 +19,8 @@ public MessageJson() Bcc = new List(); MergeData = new MergeDataJson(); Attachments = new List(); + Metadata = new List(); + Tags = new List(); } /// @@ -101,6 +103,16 @@ public MessageJson() /// Gets or sets the list of merge data. /// public MergeDataJson MergeData { get; set; } + + /// + /// A list of metadata headers added to the message. + /// + public List Metadata { get; set; } + + /// + /// A list of tag headers added to the message. + /// + public List Tags { get; set; } #region Conditional Property Serialization @@ -170,5 +182,6 @@ public bool ShouldSerializeAttachment() } #endregion + } } \ No newline at end of file diff --git a/src/SocketLabs/InjectionApi/Core/Serialization/MetadataHeaderJson.cs b/src/SocketLabs/InjectionApi/Core/Serialization/MetadataHeaderJson.cs new file mode 100644 index 0000000..98685fe --- /dev/null +++ b/src/SocketLabs/InjectionApi/Core/Serialization/MetadataHeaderJson.cs @@ -0,0 +1,30 @@ +namespace SocketLabs.InjectionApi.Core.Serialization +{ + /// + /// Represents a metadata item as a key and value pair. + /// To be serialized into JSON string before sending to the Injection Api. + /// + internal class MetadataHeaderJson + { + /// + /// Creates a new instance of the MetadataHeaderJson class and sets the key and value pair. + /// + /// The key of your custom header. + /// The value for your custom header. + public MetadataHeaderJson(string key, string value) + { + Key = key; + Value = value; + } + + /// + /// Gets or sets the metadata key. + /// + public string Key { get; set; } + + /// + /// Gets or sets the metadata value. + /// + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/SocketLabs/InjectionApi/Message/BasicMessage.cs b/src/SocketLabs/InjectionApi/Message/BasicMessage.cs index d315882..c12902d 100644 --- a/src/SocketLabs/InjectionApi/Message/BasicMessage.cs +++ b/src/SocketLabs/InjectionApi/Message/BasicMessage.cs @@ -170,6 +170,22 @@ public class BasicMessage : IBasicMessage /// public IList CustomHeaders { get; set; } = new List(); + /// + /// A list of metadata headers added to the message. + /// + /// + /// (Optional) + /// + public IList Metadata { get; set; } = new List(); + + /// + /// A list of tag headers added to the message. + /// + /// + /// (Optional) + /// + public IList Tags { get; set; } = new List(); + /// /// Returns the number of recipients and subject for the message, useful for debugging. /// diff --git a/src/SocketLabs/InjectionApi/Message/BulkMessage.cs b/src/SocketLabs/InjectionApi/Message/BulkMessage.cs index c718c46..d4cb2f1 100644 --- a/src/SocketLabs/InjectionApi/Message/BulkMessage.cs +++ b/src/SocketLabs/InjectionApi/Message/BulkMessage.cs @@ -167,6 +167,22 @@ public class BulkMessage : IBulkMessage /// public IList CustomHeaders { get; set; } = new List(); + /// + /// A list of metadata headers added to the message. + /// + /// + /// (Optional) + /// + public IList Metadata { get; set; } = new List(); + + /// + /// A list of tag headers added to the message. + /// + /// + /// (Optional) + /// + public IList Tags { get; set; } = new List(); + /// /// Returns the number of recipients and subject for the message, useful for debugging. /// diff --git a/src/SocketLabs/InjectionApi/Message/IMessageBase.cs b/src/SocketLabs/InjectionApi/Message/IMessageBase.cs index 00d2519..1b46e89 100644 --- a/src/SocketLabs/InjectionApi/Message/IMessageBase.cs +++ b/src/SocketLabs/InjectionApi/Message/IMessageBase.cs @@ -108,5 +108,22 @@ public interface IMessageBase /// (Optional) /// IList CustomHeaders { get; set; } + + + /// + /// A list of metadata headers added to the message. + /// + /// + /// (Optional) + /// + IList Metadata { get; set; } + + /// + /// A list of tag headers added to the message. + /// + /// + /// (Optional) + /// + IList Tags { get; set; } } } \ No newline at end of file diff --git a/src/SocketLabs/InjectionApi/Message/IMetadata.cs b/src/SocketLabs/InjectionApi/Message/IMetadata.cs new file mode 100644 index 0000000..b2d6b50 --- /dev/null +++ b/src/SocketLabs/InjectionApi/Message/IMetadata.cs @@ -0,0 +1,35 @@ +namespace SocketLabs.InjectionApi.Message +{ + + /// + /// Represents a metadata item as a key and value pair. + /// + /// + /// Using extension methods + /// + /// var metadata = new ]]>(); + /// metadata.Add("key1", "value1"); + /// metadata.Add("key2", "value2"); + /// + /// + /// + /// + public interface IMetadata + { + /// + /// Gets or sets the metadata key. + /// + string Key { get; set; } + + /// + /// Gets or sets the metadata value. + /// + string Value { get; set; } + + /// + /// A quick check to ensure that the metadata item is valid. + /// + bool IsValid { get; } + } + +} diff --git a/src/SocketLabs/InjectionApi/Message/Metadata.cs b/src/SocketLabs/InjectionApi/Message/Metadata.cs new file mode 100644 index 0000000..7356b66 --- /dev/null +++ b/src/SocketLabs/InjectionApi/Message/Metadata.cs @@ -0,0 +1,78 @@ +namespace SocketLabs.InjectionApi.Message +{ + /// + /// Represents a metadata item as a key and value pair. + /// + /// + /// Using the constructors + /// + /// var metadata1 = new Metadata(); + /// metadata1.Key = "key1"; + /// metadata1.Value = "value1"; + /// + /// var metadata2 = new Metadata("key1", "value1"); + /// + /// Using extension methods + /// + /// var metadata = new ]]>(); + /// metadata.Add("key1", "value1"); + /// metadata.Add("key2", "value2"); + /// + /// + /// + /// + public class Metadata : IMetadata + { + /// + /// Creates a new instance of the Metadata class. + /// + /// + /// + /// var metadata = new Metadata(); + /// metadata.Name = "key1"; + /// metadata.Value = "value1"; + /// + /// + public Metadata() { } + + /// + /// Creates a new instance of the Metadata class and sets the name and value pair. + /// + /// The name of your metadata header. + /// The value for your metadata header. + /// + /// + /// var metadata = new Metadata("name1", "value1"); + /// + /// + public Metadata(string key, string value) + { + Key = key; + Value = value; + } + + /// + /// Gets or sets the metadata key. + /// + public string Key { get; set; } + + /// + /// Gets or sets the metadata value. + /// + public string Value { get; set; } + + /// + /// A quick check to ensure that the metadata entry is valid. + /// + public bool IsValid => !(string.IsNullOrEmpty(Value) || string.IsNullOrEmpty(Key)); + + /// + /// Returns the metadata header as a key-value pair, useful for debugging. + /// + /// + public override string ToString() + { + return $"{Key ?? "null"}: {Value ?? "null"}"; + } + } +} \ No newline at end of file diff --git a/src/SocketLabs/InjectionApi/SendResult.cs b/src/SocketLabs/InjectionApi/SendResult.cs index 7fc3ee7..ff28c4d 100644 --- a/src/SocketLabs/InjectionApi/SendResult.cs +++ b/src/SocketLabs/InjectionApi/SendResult.cs @@ -189,5 +189,10 @@ public enum SendResult /// Invalid custom headers found /// MessageValidationInvalidCustomHeaders, + + /// + /// Invalid metadata found + /// + MessageValidationInvalidMetadata, } } \ No newline at end of file diff --git a/src/SocketLabs/InjectionApi/SocketLabsExtensions.cs b/src/SocketLabs/InjectionApi/SocketLabsExtensions.cs index ce227f4..ecb93a0 100644 --- a/src/SocketLabs/InjectionApi/SocketLabsExtensions.cs +++ b/src/SocketLabs/InjectionApi/SocketLabsExtensions.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using Newtonsoft.Json.Linq; using SocketLabs.InjectionApi.Message; namespace SocketLabs.InjectionApi @@ -397,5 +398,71 @@ public static ICustomHeader Add(this IList source, string name, s } #endregion + + + #region List of Metadata + + /// + /// Adds a new metadata item to an existing list of metadata items. + /// + /// The existing list of metadata item. + /// The key of the new metadata item. + /// The value for the new metadata item. + /// Instance of + /// + /// + /// var metadata = new ]]>(); + /// metadata.Add("key1", "value1"); + /// metadata.Add("key2", "value2"); + /// + /// + public static IMetadata Add(this IList source, string key, string value) + { + var metadata = new Metadata(key, value); + source.Add(metadata); + return metadata; + } + + /// + /// Adds a new metadata item to an existing list of metadata items. + /// + /// The existing list of metadata item. + /// The list of new metadata items to add. + /// Instance of + /// + /// + /// var metadata = new ]]>(); + /// metadata.Add("key1", "value1"); + /// metadata.Add("key2", "value2"); + /// + /// + public static IList Add(this IList source, List list) + { + foreach (var metadata in list) + { + source.Add(metadata); + } + return list; + } + + /// + /// Adds a new metadata item to an existing list of metadata items. + /// + /// The existing list of metadata item. + /// A new metadata items to add. + /// Instance of + /// + /// + /// var metadata = new ]]>(); + /// metadata.Add("key1", "value1"); + /// metadata.Add("key2", "value2"); + /// + /// + public static IMetadata Add(this IList source, IMetadata item) + { + source.Add(item); + return item; + } + #endregion } } diff --git a/src/SocketLabs/SocketLabs.csproj b/src/SocketLabs/SocketLabs.csproj index a2b2f6c..8bd6648 100644 --- a/src/SocketLabs/SocketLabs.csproj +++ b/src/SocketLabs/SocketLabs.csproj @@ -8,12 +8,12 @@ SocketLabs.EmailDelivery false false - 1.3.0 - Copyright © 2018-2022 SocketLabs Acquisition LLC + 1.4.0 + Copyright © 2018-2023 SocketLabs Acquisition LLC Joe Cooper, David Schrenker, Matt Reibach, Ryan Lydzinski, Mike Goodfellow, Saranya Kavuri SocketLabs .Net Client Library - 1.3.0 - 1.3.0 + 1.4.0 + 1.4.0 Easily send email messages using the SocketLabs Injection API. https://github.com/socketlabs/socketlabs-csharp https://inject.docs.socketlabs.com/ diff --git a/test/SocketLabs.Tests/SocketLabs.Tests.csproj b/test/SocketLabs.Tests/SocketLabs.Tests.csproj index d3105b4..3c80746 100644 --- a/test/SocketLabs.Tests/SocketLabs.Tests.csproj +++ b/test/SocketLabs.Tests/SocketLabs.Tests.csproj @@ -6,18 +6,22 @@ SocketLabs.Tests false SocketLabs.Tests - Copyright © 2018-2022 SocketLabs Acquisition LLC + Copyright © 2018-2023 SocketLabs Acquisition LLC - + - - + + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/test/SocketLabs.Tests/Validation/SendValidatorTest.cs b/test/SocketLabs.Tests/Validation/SendValidatorTest.cs index e0b6770..f330c38 100644 --- a/test/SocketLabs.Tests/Validation/SendValidatorTest.cs +++ b/test/SocketLabs.Tests/Validation/SendValidatorTest.cs @@ -912,6 +912,92 @@ public void HasValidCustomHeaders_ReturnsTrue_WhenDictionaryIsValid() #endregion + #region HasValidMetadataHeaders + + [TestMethod] + public void HasValidMetadataHeaders_ReturnsFalse_WhenKeyAndValueAreEmpty() + { + //Arrange + var customHeaders = new List + { + new Metadata(string.Empty, string.Empty) + }; + var validator = new SendValidator(); + + //Act + var actual = validator.HasValidMetadata(customHeaders); + + //Assert + Assert.IsFalse(actual); + } + + [TestMethod] + public void HasValidMetadataHeaders_ReturnsFalse_WhenKeyIsNotEmptyAndValueIsEmpty() + { + //Arrange + var customHeaders = new List + { + new Metadata(RandomHelper.RandomString(), string.Empty) + }; + var validator = new SendValidator(); + + //Act + var actual = validator.HasValidMetadata(customHeaders); + + //Assert + Assert.IsFalse(actual); + } + + [TestMethod] + public void HasValidMetadataHeaders_ReturnsFalse_WhenKeyIsEmptyAndValueIsNotEmpty() + { + //Arrange + var customHeaders = new List + { + new Metadata(string.Empty, RandomHelper.RandomString()) + }; + var validator = new SendValidator(); + + //Act + var actual = validator.HasValidMetadata(customHeaders); + + //Assert + Assert.IsFalse(actual); + } + + + [TestMethod] + public void HasValidMetadataHeaders_ReturnsTrue_WhenDictionaryIsNull() + { + //Arrange + var customHeaders = new List(); + var validator = new SendValidator(); + + //Act + var actual = validator.HasValidMetadata(customHeaders); + + //Assert + Assert.IsTrue(actual); + } + + [TestMethod] + public void HasValidMetadataHeaders_ReturnsTrue_WhenDictionaryIsValid() + { + //Arrange + var customHeaders = new List + { + new Metadata(RandomHelper.RandomString(), RandomHelper.RandomString()) + }; + var validator = new SendValidator(); + + //Act + var actual = validator.HasValidMetadata(customHeaders); + + //Assert + Assert.IsTrue(actual); + } + + #endregion #region HasInvalidRecipients(IBasicMessage message)