From 3e08f8ba68182ec6990fbbeef7413051d6fbee05 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Thu, 19 Jan 2017 15:02:58 -0500 Subject: [PATCH 01/10] Add sink domain handling to DataMapper --- src/SparkPost/DataMapper.cs | 2 ++ src/SparkPost/SinkHandling.cs | 35 ++++++++++++++++++++++++++++++++++ src/SparkPost/SparkPost.csproj | 1 + 3 files changed, 38 insertions(+) create mode 100644 src/SparkPost/SinkHandling.cs diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index 22f87bfe..36dcfd26 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -95,6 +95,8 @@ public virtual IDictionary ToDictionary(Transmission transmissio var result = WithCommonConventions(transmission, data); + //if something + SinkHandling.AddSinkDomainToAddresses(result); CcHandling.SetAnyCCsInTheHeader(transmission, result); return result; diff --git a/src/SparkPost/SinkHandling.cs b/src/SparkPost/SinkHandling.cs new file mode 100644 index 00000000..40b39d1a --- /dev/null +++ b/src/SparkPost/SinkHandling.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SparkPost +{ + internal static class SinkHandling + { + private static readonly string SINK_DOMAIN = ".sink.sparkpostmail.com"; + + internal static void AddSinkDomainToAddresses(IDictionary result) + { + if (result.ContainsKey("recipients")) + { + var recipients = result["recipients"] as IEnumerable>; + var addresses = recipients.Select(r => r["address"]).Cast>(); + foreach (var address in addresses) + { + address["email"] = AddSinkDomainToAddress((string)address["email"]); + address["header_to"] = AddSinkDomainToAddress((string)address["header_to"]); + } + } + } + + private static string AddSinkDomainToAddress(string emailAddress) + { + if (!String.IsNullOrWhiteSpace(emailAddress) && !emailAddress.EndsWith(SINK_DOMAIN, true, null)) + return emailAddress + SINK_DOMAIN; + else + return emailAddress; + } + } +} diff --git a/src/SparkPost/SparkPost.csproj b/src/SparkPost/SparkPost.csproj index 0354bcc6..d82190b4 100644 --- a/src/SparkPost/SparkPost.csproj +++ b/src/SparkPost/SparkPost.csproj @@ -93,6 +93,7 @@ + From 97eda07ddfb38c5ada4c82d76db7e63c4a846584 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Thu, 19 Jan 2017 15:50:25 -0500 Subject: [PATCH 02/10] Add Options.UseSink and tests --- src/SparkPost.Tests/DataMapperTests.cs | 50 ++++++++++++++++++++++++++ src/SparkPost/DataMapper.cs | 2 +- src/SparkPost/Options.cs | 1 + src/SparkPost/SinkHandling.cs | 29 ++++++++------- 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/SparkPost.Tests/DataMapperTests.cs b/src/SparkPost.Tests/DataMapperTests.cs index 84d9798b..b29fece5 100644 --- a/src/SparkPost.Tests/DataMapperTests.cs +++ b/src/SparkPost.Tests/DataMapperTests.cs @@ -209,6 +209,56 @@ public void It_should_set_the_recipients() ["email"].ShouldEqual(recipient2.Address.Email); } + [TestCaseSource("SinkifyCases")] + public void It_should_sinkify_addresses(string email, string headerTo, + string expectedEmail, string expectedHeaderTo) + { + var address = new Address() { Email = email, HeaderTo = headerTo }; + transmission.Recipients.Add(new Recipient() { Address = address }); + transmission.Options.UseSink = true; + + var result = mapper.ToDictionary(transmission)["recipients"] as IEnumerable>; + + result.Count().ShouldEqual(1); + var resultAddress = result.Single()["address"].CastAs>(); + resultAddress["email"].ShouldEqual(expectedEmail); + if (expectedHeaderTo != null) + resultAddress["header_to"].ShouldEqual(expectedHeaderTo); + } + + static object[] SinkifyCases = + { + new string[] + { + "bob@example.com", + "larry@example.com", + "bob@example.com.sink.sparkpostmail.com", + "larry@example.com.sink.sparkpostmail.com" + }, + new string[] + { + "bob@example.com", + null, + "bob@example.com.sink.sparkpostmail.com", + null + }, + new string[] + { + "bob@example.com.sink.sparkpostmail.com", + "larry@example.com", + "bob@example.com.sink.sparkpostmail.com", + "larry@example.com.sink.sparkpostmail.com" + }, + new string[] + { + "bob@example.com.SINK.SPARKPOSTMAIL.COM", + "larry@example.com", + "bob@example.com.SINK.SPARKPOSTMAIL.COM", + "larry@example.com.sink.sparkpostmail.com" + }, + + }; + [Test] public void It_should_set_the_recipients_to_a_list_id_if_a_list_id_is_provided() { diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index 36dcfd26..1391ed6c 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -95,7 +95,7 @@ public virtual IDictionary ToDictionary(Transmission transmissio var result = WithCommonConventions(transmission, data); - //if something + if (transmission.Options.UseSink) SinkHandling.AddSinkDomainToAddresses(result); CcHandling.SetAnyCCsInTheHeader(transmission, result); diff --git a/src/SparkPost/Options.cs b/src/SparkPost/Options.cs index 3c90d2c6..bcc6ba5c 100644 --- a/src/SparkPost/Options.cs +++ b/src/SparkPost/Options.cs @@ -12,5 +12,6 @@ public class Options public bool? SkipSuppression { get; set; } public bool? InlineCss { get; set; } public string IpPool { get; set; } + public bool UseSink { get; set; } } } \ No newline at end of file diff --git a/src/SparkPost/SinkHandling.cs b/src/SparkPost/SinkHandling.cs index 40b39d1a..f55e2df9 100644 --- a/src/SparkPost/SinkHandling.cs +++ b/src/SparkPost/SinkHandling.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SparkPost { @@ -14,22 +12,29 @@ internal static void AddSinkDomainToAddresses(IDictionary result { if (result.ContainsKey("recipients")) { - var recipients = result["recipients"] as IEnumerable>; - var addresses = recipients.Select(r => r["address"]).Cast>(); - foreach (var address in addresses) + var recipients = (result["recipients"] as IEnumerable>).ToList(); + foreach (var recipient in recipients) { - address["email"] = AddSinkDomainToAddress((string)address["email"]); - address["header_to"] = AddSinkDomainToAddress((string)address["header_to"]); + AddSinkDomainToAddress(recipient, "email"); + AddSinkDomainToAddress(recipient, "header_to"); } + result["recipients"] = recipients; } } - private static string AddSinkDomainToAddress(string emailAddress) + private static void AddSinkDomainToAddress(IDictionary recipient, string fieldName) { - if (!String.IsNullOrWhiteSpace(emailAddress) && !emailAddress.EndsWith(SINK_DOMAIN, true, null)) - return emailAddress + SINK_DOMAIN; - else - return emailAddress; + if (recipient.ContainsKey("address")) + { + var address = recipient["address"] as IDictionary; + if (address.ContainsKey(fieldName)) + { + var emailAddress = (string)address[fieldName]; + if (!String.IsNullOrWhiteSpace(emailAddress) && !emailAddress.EndsWith(SINK_DOMAIN, true, null)) + address[fieldName] = emailAddress + SINK_DOMAIN; + + } + } } } } From 4a9ddb2d5c2d8c838b24c12cf2f035bee0696be4 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Thu, 19 Jan 2017 17:18:11 -0500 Subject: [PATCH 03/10] Fix failing test Always remove UseSink from Options mapping --- src/SparkPost/DataMapper.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index 1391ed6c..a73b787b 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -131,7 +131,9 @@ public virtual IDictionary ToDictionary(Address address) public virtual IDictionary ToDictionary(Options options) { - return AnyValuesWereSetOn(options) ? WithCommonConventions(options) : null; + var result = WithCommonConventions(options); + result.Remove("use_sink"); + return result.Any() ? result : null; } public virtual IDictionary ToDictionary(Content content) From 57bc4b53df359a994a88fcdfe8d106827d95b92c Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Thu, 19 Jan 2017 17:32:05 -0500 Subject: [PATCH 04/10] Add a test case and refactor test --- src/SparkPost.Tests/DataMapperTests.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/SparkPost.Tests/DataMapperTests.cs b/src/SparkPost.Tests/DataMapperTests.cs index b29fece5..a005fc82 100644 --- a/src/SparkPost.Tests/DataMapperTests.cs +++ b/src/SparkPost.Tests/DataMapperTests.cs @@ -221,9 +221,15 @@ public void It_should_sinkify_addresses(string email, string headerTo, result.Count().ShouldEqual(1); var resultAddress = result.Single()["address"].CastAs>(); - resultAddress["email"].ShouldEqual(expectedEmail); - if (expectedHeaderTo != null) - resultAddress["header_to"].ShouldEqual(expectedHeaderTo); + + foreach (var fieldToTest in new Dictionary() { + { "email", expectedEmail }, { "header_to", expectedHeaderTo} }) + { + if (fieldToTest.Value == null) + resultAddress.ContainsKey(fieldToTest.Key).ShouldBeFalse(); + else + resultAddress[fieldToTest.Key].ShouldEqual(fieldToTest.Value); + } } static object[] SinkifyCases = @@ -256,7 +262,10 @@ public void It_should_sinkify_addresses(string email, string headerTo, "bob@example.com.SINK.SPARKPOSTMAIL.COM", "larry@example.com.sink.sparkpostmail.com" }, - + new string[] + { + null, null, null, null + }, }; [Test] From 4278abdf6ca728301f6fc11e541f627f62b3222e Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Thu, 19 Jan 2017 22:06:16 -0500 Subject: [PATCH 05/10] More idiomatic way to keep UseSink out of mapping --- src/SparkPost/DataMapper.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index a73b787b..8298f078 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -131,8 +131,11 @@ public virtual IDictionary ToDictionary(Address address) public virtual IDictionary ToDictionary(Options options) { - var result = WithCommonConventions(options); - result.Remove("use_sink"); + var result = WithCommonConventions(options, new Dictionary() + { + ["use_sink"] = null, + }); + return result.Any() ? result : null; } From 3726b5a21933d6a1591192d7df5c3a0c54df8523 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Fri, 20 Jan 2017 22:22:39 -0500 Subject: [PATCH 06/10] Refactor a little --- src/SparkPost/SinkHandling.cs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/SparkPost/SinkHandling.cs b/src/SparkPost/SinkHandling.cs index f55e2df9..e908328a 100644 --- a/src/SparkPost/SinkHandling.cs +++ b/src/SparkPost/SinkHandling.cs @@ -13,28 +13,27 @@ internal static void AddSinkDomainToAddresses(IDictionary result if (result.ContainsKey("recipients")) { var recipients = (result["recipients"] as IEnumerable>).ToList(); - foreach (var recipient in recipients) + foreach (var address in recipients + .Where(r => r.ContainsKey("address")) + .Select(r => r["address"]) + .Cast>()) { - AddSinkDomainToAddress(recipient, "email"); - AddSinkDomainToAddress(recipient, "header_to"); + AddSinkDomainToAddress(address, "email"); + AddSinkDomainToAddress(address, "header_to"); } result["recipients"] = recipients; } } - private static void AddSinkDomainToAddress(IDictionary recipient, string fieldName) - { - if (recipient.ContainsKey("address")) + private static void AddSinkDomainToAddress(IDictionary address, string fieldName) + { + if (address.ContainsKey(fieldName)) { - var address = recipient["address"] as IDictionary; - if (address.ContainsKey(fieldName)) - { - var emailAddress = (string)address[fieldName]; - if (!String.IsNullOrWhiteSpace(emailAddress) && !emailAddress.EndsWith(SINK_DOMAIN, true, null)) - address[fieldName] = emailAddress + SINK_DOMAIN; + var currentAddress = (string)address[fieldName]; + if (!String.IsNullOrWhiteSpace(currentAddress) && !currentAddress.EndsWith(SINK_DOMAIN, true, null)) + address[fieldName] = currentAddress + SINK_DOMAIN; - } - } + } } } } From ade0ab76c13bcb596a250d4faf7d1e7155dbc4f3 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Tue, 24 Jan 2017 11:36:56 -0500 Subject: [PATCH 07/10] Fix merge error --- src/SparkPost/DataMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index 8298f078..93a11b3a 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -97,7 +97,7 @@ public virtual IDictionary ToDictionary(Transmission transmissio if (transmission.Options.UseSink) SinkHandling.AddSinkDomainToAddresses(result); - CcHandling.SetAnyCCsInTheHeader(transmission, result); + CcHandling.Process(transmission, result); return result; } From f759c1095c9dde9bd4282bd495e819c3ff876305 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Tue, 24 Jan 2017 18:55:18 -0500 Subject: [PATCH 08/10] Add Client.Custom.SinkAllTransmissions and tests --- src/SparkPost.Tests/DataMapperTests.cs | 22 +++++++++++++++++++++- src/SparkPost/Client.cs | 1 + src/SparkPost/DataMapper.cs | 8 +++++++- src/SparkPost/Transmissions.cs | 4 ++-- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/SparkPost.Tests/DataMapperTests.cs b/src/SparkPost.Tests/DataMapperTests.cs index f5dd794b..a16d568b 100644 --- a/src/SparkPost.Tests/DataMapperTests.cs +++ b/src/SparkPost.Tests/DataMapperTests.cs @@ -211,7 +211,7 @@ public void It_should_set_the_recipients() [TestCaseSource("SinkifyCases")] public void It_should_sinkify_addresses(string email, string headerTo, - string expectedEmail, string expectedHeaderTo) + string expectedEmail, string expectedHeaderTo) { var address = new Address() { Email = email, HeaderTo = headerTo }; transmission.Recipients.Add(new Recipient() { Address = address }); @@ -268,6 +268,26 @@ public void It_should_sinkify_addresses(string email, string headerTo, }, }; + [TestCase(true, true, true)] + [TestCase(true, false, true)] + [TestCase(false, true, true)] + [TestCase(false, false, false)] + public void Client_sink_setting_should_override_transmission(bool clientUseSink, + bool transmissionUseSink, bool shouldSinkify) + { + var address = new Address() { Email = Guid.NewGuid().ToString() }; + transmission.Recipients.Add(new Recipient() { Address = address }); + transmission.Options.UseSink = transmissionUseSink; + + var result = mapper.ToDictionary(transmission, clientUseSink)["recipients"] as IEnumerable>; + var resultAddress = result.Single()["address"].CastAs>(); + + resultAddress["email"] + .CastAs() + .EndsWith(".sink.sparkpostmail.com") + .ShouldNotEqual(shouldSinkify); + } + [Test] public void It_should_set_the_recipients_to_a_list_id_if_a_list_id_is_provided() { diff --git a/src/SparkPost/Client.cs b/src/SparkPost/Client.cs index 556f4ef6..4083a9f6 100644 --- a/src/SparkPost/Client.cs +++ b/src/SparkPost/Client.cs @@ -82,6 +82,7 @@ public Settings() } public SendingModes SendingMode { get; set; } + public bool SinkAllTransmissions { get; set; } public HttpClient CreateANewHttpClient() { diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index 93a11b3a..437cfdba 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -16,6 +16,7 @@ public interface IDataMapper IDictionary ToDictionary(SendingDomainStatus sendingDomainStatus); IDictionary ToDictionary(VerifySendingDomain verifySendingDomain); IDictionary ToDictionary(Transmission transmission); + IDictionary ToDictionary(Transmission transmission, bool useSink); IDictionary ToDictionary(Recipient recipient); IDictionary ToDictionary(Address address); IDictionary ToDictionary(Options options); @@ -81,6 +82,11 @@ public virtual IDictionary ToDictionary(VerifySendingDomain veri } public virtual IDictionary ToDictionary(Transmission transmission) + { + return ToDictionary(transmission, false); + } + + public virtual IDictionary ToDictionary(Transmission transmission, bool useSink) { var data = new Dictionary { @@ -95,7 +101,7 @@ public virtual IDictionary ToDictionary(Transmission transmissio var result = WithCommonConventions(transmission, data); - if (transmission.Options.UseSink) + if (useSink || transmission.Options.UseSink) SinkHandling.AddSinkDomainToAddresses(result); CcHandling.Process(transmission, result); diff --git a/src/SparkPost/Transmissions.cs b/src/SparkPost/Transmissions.cs index 42c09478..8bcecc6c 100644 --- a/src/SparkPost/Transmissions.cs +++ b/src/SparkPost/Transmissions.cs @@ -21,12 +21,12 @@ public Transmissions(Client client, IRequestSender requestSender, DataMapper dat } public async Task Send(Transmission transmission) - { + { var request = new Request { Url = $"api/{client.Version}/transmissions", Method = "POST", - Data = dataMapper.ToDictionary(transmission) + Data = dataMapper.ToDictionary(transmission, client.CustomSettings.SinkAllTransmissions) }; var response = await requestSender.Send(request); From de25cb00ed31a8f70dfa53c4f93b13764188b6ff Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Tue, 24 Jan 2017 19:33:05 -0500 Subject: [PATCH 09/10] Fix backwards test --- src/SparkPost.Tests/DataMapperTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SparkPost.Tests/DataMapperTests.cs b/src/SparkPost.Tests/DataMapperTests.cs index a16d568b..f287c64a 100644 --- a/src/SparkPost.Tests/DataMapperTests.cs +++ b/src/SparkPost.Tests/DataMapperTests.cs @@ -285,7 +285,7 @@ public void Client_sink_setting_should_override_transmission(bool clientUseSink, resultAddress["email"] .CastAs() .EndsWith(".sink.sparkpostmail.com") - .ShouldNotEqual(shouldSinkify); + .ShouldEqual(shouldSinkify); } [Test] From 9acf547eb43c1659762373672442dcc600a6b8d8 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Tue, 24 Jan 2017 19:33:22 -0500 Subject: [PATCH 10/10] Rename flag --- src/SparkPost.Tests/DataMapperTests.cs | 4 ++-- src/SparkPost/DataMapper.cs | 6 +++--- src/SparkPost/Options.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SparkPost.Tests/DataMapperTests.cs b/src/SparkPost.Tests/DataMapperTests.cs index f287c64a..43eaeb89 100644 --- a/src/SparkPost.Tests/DataMapperTests.cs +++ b/src/SparkPost.Tests/DataMapperTests.cs @@ -215,7 +215,7 @@ public void It_should_sinkify_addresses(string email, string headerTo, { var address = new Address() { Email = email, HeaderTo = headerTo }; transmission.Recipients.Add(new Recipient() { Address = address }); - transmission.Options.UseSink = true; + transmission.Options.Sink = true; var result = mapper.ToDictionary(transmission)["recipients"] as IEnumerable>; @@ -277,7 +277,7 @@ public void Client_sink_setting_should_override_transmission(bool clientUseSink, { var address = new Address() { Email = Guid.NewGuid().ToString() }; transmission.Recipients.Add(new Recipient() { Address = address }); - transmission.Options.UseSink = transmissionUseSink; + transmission.Options.Sink = transmissionUseSink; var result = mapper.ToDictionary(transmission, clientUseSink)["recipients"] as IEnumerable>; var resultAddress = result.Single()["address"].CastAs>(); diff --git a/src/SparkPost/DataMapper.cs b/src/SparkPost/DataMapper.cs index 437cfdba..77f0933e 100644 --- a/src/SparkPost/DataMapper.cs +++ b/src/SparkPost/DataMapper.cs @@ -86,7 +86,7 @@ public virtual IDictionary ToDictionary(Transmission transmissio return ToDictionary(transmission, false); } - public virtual IDictionary ToDictionary(Transmission transmission, bool useSink) + public virtual IDictionary ToDictionary(Transmission transmission, bool sink) { var data = new Dictionary { @@ -101,7 +101,7 @@ public virtual IDictionary ToDictionary(Transmission transmissio var result = WithCommonConventions(transmission, data); - if (useSink || transmission.Options.UseSink) + if (sink || transmission.Options.Sink) SinkHandling.AddSinkDomainToAddresses(result); CcHandling.Process(transmission, result); @@ -139,7 +139,7 @@ public virtual IDictionary ToDictionary(Options options) { var result = WithCommonConventions(options, new Dictionary() { - ["use_sink"] = null, + ["sink"] = null, }); return result.Any() ? result : null; diff --git a/src/SparkPost/Options.cs b/src/SparkPost/Options.cs index bcc6ba5c..6a1905ba 100644 --- a/src/SparkPost/Options.cs +++ b/src/SparkPost/Options.cs @@ -12,6 +12,6 @@ public class Options public bool? SkipSuppression { get; set; } public bool? InlineCss { get; set; } public string IpPool { get; set; } - public bool UseSink { get; set; } + public bool Sink { get; set; } } } \ No newline at end of file