From 3e460a144a5bebdb1de309329ffc0779c5ee3d74 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 11 Apr 2021 12:06:07 -0400 Subject: [PATCH 01/32] Allow sender to be personalized when sending email to multiple recipients Resolves #380 --- Source/StrongGrid.IntegrationTests/Tests/Mail.cs | 1 + Source/StrongGrid/Models/MailPersonalization.cs | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/StrongGrid.IntegrationTests/Tests/Mail.cs b/Source/StrongGrid.IntegrationTests/Tests/Mail.cs index f1f0e0b1..c52a1b07 100644 --- a/Source/StrongGrid.IntegrationTests/Tests/Mail.cs +++ b/Source/StrongGrid.IntegrationTests/Tests/Mail.cs @@ -27,6 +27,7 @@ public async Task RunAsync(IBaseClient client, TextWriter log, CancellationToken { new MailPersonalization { + From = new MailAddress("alternate_sender@example.com", "Alternate Sender"), To = new[] { to1, to1 }, Cc = new[] { to1 }, Bcc = new[] { to1 }, diff --git a/Source/StrongGrid/Models/MailPersonalization.cs b/Source/StrongGrid/Models/MailPersonalization.cs index dad668b6..fd7b8d61 100644 --- a/Source/StrongGrid/Models/MailPersonalization.cs +++ b/Source/StrongGrid/Models/MailPersonalization.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using StrongGrid.Utilities; using System; using System.Collections.Generic; @@ -13,6 +13,16 @@ namespace StrongGrid.Models /// public class MailPersonalization { + /// + /// Gets or sets the 'From' email address used to deliver the message. + /// This address should be a verified sender in your Twilio SendGrid account. + /// + /// + /// From. + /// + [JsonProperty("from", NullValueHandling = NullValueHandling.Ignore)] + public MailAddress From { get; set; } + /// /// Gets or sets an array of recipients. /// Each object within this array may contain the recipient’s From b9e48168d39261a2cdfd0fdf8e46fcfa5c4cc9a1 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 11 Apr 2021 14:58:52 -0400 Subject: [PATCH 02/32] Fix Contacts.ImportFromStreamAsync Resolves #384 --- Source/StrongGrid/Resources/Contacts.cs | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Source/StrongGrid/Resources/Contacts.cs b/Source/StrongGrid/Resources/Contacts.cs index 8b22a86f..c0589083 100644 --- a/Source/StrongGrid/Resources/Contacts.cs +++ b/Source/StrongGrid/Resources/Contacts.cs @@ -316,15 +316,15 @@ public Task SearchAsync(IEnumerable - /// Request all contacts to be exported. + /// Import contacts. /// - /// Use the "job id" returned by this method with the CheckExportJobStatusAsync - /// method to verify if the export job is completed. + /// Use the "job id" returned by this method with the GetImportJobAsync + /// method to verify if the import job is completed. /// /// The stream containing the data to import. - /// File type for export file. Choose from json or csv. + /// File type for import file. Choose from json or csv. /// List of field_definition IDs to map the uploaded CSV columns to. For example, [null, "w1", "_rf1"] means to skip Col[0], map Col[1] => CustomField w1, map Col[2] => ReservedField _rf1. - /// Ids of the contact lists you want to export. + /// All contacts will be added to each of the specified lists. /// The cancellation token. /// /// The job id. @@ -333,7 +333,7 @@ public async Task ImportFromStreamAsync(Stream stream, FileType fileType { var data = new JObject(); data.AddPropertyIfValue("list_ids", listIds); - data.AddPropertyIfValue("file_type", fileType); + data.AddPropertyIfEnumValue("file_type", (Parameter)fileType); data.AddPropertyIfValue("field_mappings", fieldsMapping); var importRequest = await _client @@ -343,15 +343,19 @@ public async Task ImportFromStreamAsync(Stream stream, FileType fileType .AsRawJsonObject() .ConfigureAwait(false); - var importJobId = importRequest["id"].Value(); - var uploadUrl = importRequest["upload_url"].Value(); - var uploadHeaders = importRequest["upload_headers"].Value[]>(); + var importJobId = importRequest.GetPropertyValue("job_id"); + var uploadUrl = importRequest.GetPropertyValue("upload_uri"); + var uploadHeaders = ((JArray)importRequest["upload_headers"]) + .Select(hdr => new KeyValuePair(hdr.GetPropertyValue("header"), hdr.GetPropertyValue("value"))) + .ToArray(); - var request = new HttpRequestMessage(HttpMethod.Post, uploadUrl) + var request = new HttpRequestMessage(HttpMethod.Put, uploadUrl) { Content = new StreamContent(stream) }; - request.Headers.Clear(); + + request.Headers.Add("User-Agent", _client.BaseClient.DefaultRequestHeaders.UserAgent.First().ToString()); + foreach (var header in uploadHeaders) { request.Headers.Add(header.Key, header.Value); @@ -359,7 +363,8 @@ public async Task ImportFromStreamAsync(Stream stream, FileType fileType using (var client = new HttpClient()) { - await client.SendAsync(request, cancellationToken).ConfigureAwait(false); + var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); + if (!response.IsSuccessStatusCode) throw new SendGridException($"File upload failed: {response.ReasonPhrase}", response, "Diagnostic log unavailable"); } return importJobId; From c641e1d59ed3fb07ac00c894aff397046c12e42f Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 11 Apr 2021 15:01:08 -0400 Subject: [PATCH 03/32] Compress the file being uploaded Resolves #381 --- Source/StrongGrid/Extensions/Internal.cs | 31 +++++++++++++++++------- Source/StrongGrid/Resources/Contacts.cs | 3 ++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Source/StrongGrid/Extensions/Internal.cs b/Source/StrongGrid/Extensions/Internal.cs index 4d027ec0..b8a9d62a 100644 --- a/Source/StrongGrid/Extensions/Internal.cs +++ b/Source/StrongGrid/Extensions/Internal.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -279,16 +280,16 @@ internal static async Task> AsPaginatedResponse(this IRe internal static IRequest WithJsonBody(this IRequest request, T body, bool omitCharSet = false) { return request.WithBody(bodyBuilder => - { - var httpContent = bodyBuilder.Model(body, new MediaTypeHeaderValue("application/json")); + { + var httpContent = bodyBuilder.Model(body, new MediaTypeHeaderValue("application/json")); - if (omitCharSet && !string.IsNullOrEmpty(httpContent.Headers.ContentType.CharSet)) - { - httpContent.Headers.ContentType.CharSet = string.Empty; - } + if (omitCharSet && !string.IsNullOrEmpty(httpContent.Headers.ContentType.CharSet)) + { + httpContent.Headers.ContentType.CharSet = string.Empty; + } - return httpContent; - }); + return httpContent; + }); } /// @@ -652,6 +653,18 @@ internal static void CheckForSendGridErrors(this IResponse response) } } + internal static async Task CompressAsync(this Stream source) + { + var compressedStream = new MemoryStream(); + using (var gzip = new GZipStream(compressedStream, CompressionMode.Compress, true)) + { + await source.CopyToAsync(gzip).ConfigureAwait(false); + } + + compressedStream.Position = 0; + return compressedStream; + } + private static async Task<(bool, string)> GetErrorMessage(HttpResponseMessage message) { // Assume there is no error @@ -696,7 +709,7 @@ internal static void CheckForSendGridErrors(this IResponse response) } I have also seen cases where the JSON string looks like this: - { + { "error": "Name already exists" } */ diff --git a/Source/StrongGrid/Resources/Contacts.cs b/Source/StrongGrid/Resources/Contacts.cs index c0589083..3c35d7e3 100644 --- a/Source/StrongGrid/Resources/Contacts.cs +++ b/Source/StrongGrid/Resources/Contacts.cs @@ -351,9 +351,10 @@ public async Task ImportFromStreamAsync(Stream stream, FileType fileType var request = new HttpRequestMessage(HttpMethod.Put, uploadUrl) { - Content = new StreamContent(stream) + Content = new StreamContent(await stream.CompressAsync().ConfigureAwait(false)) }; + request.Headers.AcceptEncoding.TryParseAdd("gzip"); request.Headers.Add("User-Agent", _client.BaseClient.DefaultRequestHeaders.UserAgent.First().ToString()); foreach (var header in uploadHeaders) From f9fb14b388f55df66ad8e53c6cfc695ce9f1b143 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 11 Apr 2021 15:02:50 -0400 Subject: [PATCH 04/32] Allow specifying precision when converting DateTime to/from Unix time --- Source/StrongGrid/Extensions/Internal.cs | 31 ++++++++++++++++++------ Source/StrongGrid/Utilities/Utils.cs | 2 -- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Source/StrongGrid/Extensions/Internal.cs b/Source/StrongGrid/Extensions/Internal.cs index b8a9d62a..28cd79c2 100644 --- a/Source/StrongGrid/Extensions/Internal.cs +++ b/Source/StrongGrid/Extensions/Internal.cs @@ -23,30 +23,45 @@ namespace StrongGrid /// internal static class Internal { + internal enum UnixTimePrecision + { + Seconds = 0, + Milliseconds = 1 + } + + private static readonly DateTime EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + /// - /// Converts a 'unix time' (which is expressed as the number of seconds since midnight on - /// January 1st 1970) to a .Net . + /// Converts a 'unix time' (which is expressed as the number of seconds/milliseconds since + /// midnight on January 1st 1970) to a .Net . /// /// The unix time. + /// The desired precision. /// /// The . /// - internal static DateTime FromUnixTime(this long unixTime) + internal static DateTime FromUnixTime(this long unixTime, UnixTimePrecision precision = UnixTimePrecision.Seconds) { - return Utils.Epoch.AddSeconds(unixTime); + if (precision == UnixTimePrecision.Seconds) return EPOCH.AddSeconds(unixTime); + if (precision == UnixTimePrecision.Milliseconds) return EPOCH.AddMilliseconds(unixTime); + throw new Exception($"Unknown precision: {precision}"); } /// /// Converts a .Net into a 'Unix time' (which is expressed as the number - /// of seconds since midnight on January 1st 1970). + /// of seconds/milliseconds since midnight on January 1st 1970). /// /// The date. + /// The desired precision. /// - /// The numer of seconds since midnight on January 1st 1970. + /// The numer of seconds/milliseconds since midnight on January 1st 1970. /// - internal static long ToUnixTime(this DateTime date) + internal static long ToUnixTime(this DateTime date, UnixTimePrecision precision = UnixTimePrecision.Seconds) { - return Convert.ToInt64((date.ToUniversalTime() - Utils.Epoch).TotalSeconds); + var diff = date.ToUniversalTime() - EPOCH; + if (precision == UnixTimePrecision.Seconds) return Convert.ToInt64(diff.TotalSeconds); + if (precision == UnixTimePrecision.Milliseconds) return Convert.ToInt64(diff.TotalMilliseconds); + throw new Exception($"Unknown precision: {precision}"); } /// diff --git a/Source/StrongGrid/Utilities/Utils.cs b/Source/StrongGrid/Utilities/Utils.cs index 374c613f..8d02f510 100644 --- a/Source/StrongGrid/Utilities/Utils.cs +++ b/Source/StrongGrid/Utilities/Utils.cs @@ -12,8 +12,6 @@ internal static class Utils private static readonly byte[] Secp256R1Prefix = Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE"); private static readonly byte[] CngBlobPrefix = { 0x45, 0x43, 0x53, 0x31, 0x20, 0, 0, 0 }; - public static DateTime Epoch { get; } = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - public static RecyclableMemoryStreamManager MemoryStreamManager { get; } = new RecyclableMemoryStreamManager(); /// From 6cf278272fcc0d457bc232140e633fd8eb589aa8 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 11 Apr 2021 15:03:44 -0400 Subject: [PATCH 05/32] Remove dead code --- .../Utilities/MailPersonalizationConverter.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Source/StrongGrid/Utilities/MailPersonalizationConverter.cs b/Source/StrongGrid/Utilities/MailPersonalizationConverter.cs index 9aa646d5..f19c61e2 100644 --- a/Source/StrongGrid/Utilities/MailPersonalizationConverter.cs +++ b/Source/StrongGrid/Utilities/MailPersonalizationConverter.cs @@ -1,9 +1,8 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using StrongGrid.Models; using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; namespace StrongGrid.Utilities { @@ -71,13 +70,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s writer.WriteStartObject(); -#if NETSTANDARD1 - var props = value.GetType().GetTypeInfo().DeclaredProperties; -#else - var props = value.GetType().GetProperties(); -#endif - - foreach (var propertyInfo in props) + foreach (var propertyInfo in value.GetType().GetProperties()) { var propertyCustomAttributes = propertyInfo.GetCustomAttributes(false); var propertyConverterAttribute = propertyCustomAttributes.OfType().FirstOrDefault(); From 752d27abc3b496274970c9a86eb83fbfaa2c5dda Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 09:21:37 -0400 Subject: [PATCH 06/32] Retrieve multiple contacts in a single API call Resolves #385 --- .../Tests/ContactsAndCustomFields.cs | 7 ++++++ Source/StrongGrid/Resources/Contacts.cs | 22 +++++++++++++++++++ Source/StrongGrid/Resources/IContacts.cs | 10 +++++++++ 3 files changed, 39 insertions(+) diff --git a/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs b/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs index 335092a2..540165b4 100644 --- a/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs +++ b/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs @@ -84,6 +84,13 @@ public async Task RunAsync(IClient client, TextWriter log, CancellationToken can if (contacts.Any()) { + var batch = await client.Contacts.GetMultipleAsync(contacts.Take(10).Select(c => c.Id), cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Retrieved {batch.Length} contacts in a single API call.").ConfigureAwait(false); + foreach (var record in batch) + { + await log.WriteLineAsync($"\t{record.FirstName} {record.LastName}").ConfigureAwait(false); + } + var contact = await client.Contacts.GetAsync(contacts.First().Id).ConfigureAwait(false); await log.WriteLineAsync($"Retrieved contact {contact.Id}").ConfigureAwait(false); await log.WriteLineAsync($"\tEmail: {contact.Email}").ConfigureAwait(false); diff --git a/Source/StrongGrid/Resources/Contacts.cs b/Source/StrongGrid/Resources/Contacts.cs index 3c35d7e3..949f128c 100644 --- a/Source/StrongGrid/Resources/Contacts.cs +++ b/Source/StrongGrid/Resources/Contacts.cs @@ -226,6 +226,28 @@ public Task GetAsync(string contactId, CancellationToken cancellationTo .AsObject(); } + /// + /// Retrieve multiple contacts. + /// + /// An enumeration of contact identifiers. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetMultipleAsync(IEnumerable contactIds, CancellationToken cancellationToken = default) + { + var data = new JObject() + { + { "ids", new JArray(contactIds) } + }; + + return _client + .PostAsync($"{_endpoint}/batch") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsObject("result"); + } + /// /// Searches for contacts matching the specified conditions. /// diff --git a/Source/StrongGrid/Resources/IContacts.cs b/Source/StrongGrid/Resources/IContacts.cs index b439fa7c..22c620ac 100644 --- a/Source/StrongGrid/Resources/IContacts.cs +++ b/Source/StrongGrid/Resources/IContacts.cs @@ -127,6 +127,16 @@ Task UpsertAsync( /// Task GetAsync(string contactId, CancellationToken cancellationToken = default); + /// + /// Retrieve multiple contacts. + /// + /// An enumeration of contact identifiers. + /// The cancellation token. + /// + /// An array of . + /// + Task GetMultipleAsync(IEnumerable contactIds, CancellationToken cancellationToken = default); + /// /// Searches for contacts matching the specified conditions. /// From d10396e3e37e206e687ee94b34b3511890f29d8c Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 11:14:27 -0400 Subject: [PATCH 07/32] Retrieve multiple contacts in a single API call Resolves #382 --- .../Tests/ContactsAndCustomFields.cs | 13 +++-- Source/StrongGrid/Resources/Contacts.cs | 49 ++++++++++++++++++- Source/StrongGrid/Resources/IContacts.cs | 12 ++++- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs b/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs index 540165b4..128ac053 100644 --- a/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs +++ b/Source/StrongGrid.IntegrationTests/Tests/ContactsAndCustomFields.cs @@ -84,9 +84,16 @@ public async Task RunAsync(IClient client, TextWriter log, CancellationToken can if (contacts.Any()) { - var batch = await client.Contacts.GetMultipleAsync(contacts.Take(10).Select(c => c.Id), cancellationToken).ConfigureAwait(false); - await log.WriteLineAsync($"Retrieved {batch.Length} contacts in a single API call.").ConfigureAwait(false); - foreach (var record in batch) + var batchById = await client.Contacts.GetMultipleAsync(contacts.Take(10).Select(c => c.Id), cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Retrieved {batchById.Length} contacts by ID in a single API call.").ConfigureAwait(false); + foreach (var record in batchById) + { + await log.WriteLineAsync($"\t{record.FirstName} {record.LastName}").ConfigureAwait(false); + } + + var batchByEmailAddress = await client.Contacts.GetMultipleByEmailAddressAsync(contacts.Take(10).Select(c => c.Email), cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Retrieved {batchByEmailAddress.Length} contacts by email address in a single API call.").ConfigureAwait(false); + foreach (var record in batchByEmailAddress) { await log.WriteLineAsync($"\t{record.FirstName} {record.LastName}").ConfigureAwait(false); } diff --git a/Source/StrongGrid/Resources/Contacts.cs b/Source/StrongGrid/Resources/Contacts.cs index 949f128c..d0907dc1 100644 --- a/Source/StrongGrid/Resources/Contacts.cs +++ b/Source/StrongGrid/Resources/Contacts.cs @@ -227,7 +227,7 @@ public Task GetAsync(string contactId, CancellationToken cancellationTo } /// - /// Retrieve multiple contacts. + /// Retrieve multiple contacts by ID. /// /// An enumeration of contact identifiers. /// The cancellation token. @@ -248,6 +248,53 @@ public Task GetMultipleAsync(IEnumerable contactIds, Cancella .AsObject("result"); } + /// + /// Retrieve multiple contacts by email address. + /// + /// An enumeration of email addresses. + /// The cancellation token. + /// + /// An array of . + /// + public async Task GetMultipleByEmailAddressAsync(IEnumerable emailAdresses, CancellationToken cancellationToken = default) + { + var data = new JObject() + { + { "emails", new JArray(emailAdresses) } + }; + + var response = await _client + .PostAsync($"{_endpoint}/search/emails") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsResponse() + .ConfigureAwait(false); + + // If no contact is found, SendGrid return HTTP 404 + if (response.Status == HttpStatusCode.NotFound) + { + return Array.Empty(); + } + + var result = await response.AsRawJsonObject("result").ConfigureAwait(false); + var contacts = new List(); +#if DEBUG + var errors = new List<(string EmailAddress, string ErrorMessage)>(); +#endif + + foreach (var record in result) + { + var emailAddress = record.Key; + var resultValue = (JObject)record.Value; + if (resultValue["contact"] != null) contacts.Add(resultValue["contact"].ToObject()); +#if DEBUG + if (resultValue["error"] != null) errors.Add((emailAddress, resultValue["error"].Value())); +#endif + } + + return contacts.ToArray(); + } + /// /// Searches for contacts matching the specified conditions. /// diff --git a/Source/StrongGrid/Resources/IContacts.cs b/Source/StrongGrid/Resources/IContacts.cs index 22c620ac..4d8ac65f 100644 --- a/Source/StrongGrid/Resources/IContacts.cs +++ b/Source/StrongGrid/Resources/IContacts.cs @@ -128,7 +128,7 @@ Task UpsertAsync( Task GetAsync(string contactId, CancellationToken cancellationToken = default); /// - /// Retrieve multiple contacts. + /// Retrieve multiple contacts by ID. /// /// An enumeration of contact identifiers. /// The cancellation token. @@ -137,6 +137,16 @@ Task UpsertAsync( /// Task GetMultipleAsync(IEnumerable contactIds, CancellationToken cancellationToken = default); + /// + /// Retrieve multiple contacts by email address. + /// + /// An enumeration of email addresses. + /// The cancellation token. + /// + /// An array of . + /// + Task GetMultipleByEmailAddressAsync(IEnumerable emailAdresses, CancellationToken cancellationToken = default); + /// /// Searches for contacts matching the specified conditions. /// From 510560cdfc652a5fdc09e4670eaa0bbca511033c Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 11:27:42 -0400 Subject: [PATCH 08/32] Upgrade NuGet packages --- .../StrongGrid.IntegrationTests.csproj | 2 +- Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj | 4 ++-- Source/StrongGrid/StrongGrid.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj b/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj index ae461ff1..f1236244 100644 --- a/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj +++ b/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj @@ -15,7 +15,7 @@ - + diff --git a/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj b/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj index bb5d1166..0c553b38 100644 --- a/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj +++ b/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/Source/StrongGrid/StrongGrid.csproj b/Source/StrongGrid/StrongGrid.csproj index c0df2d6c..524366a5 100644 --- a/Source/StrongGrid/StrongGrid.csproj +++ b/Source/StrongGrid/StrongGrid.csproj @@ -35,11 +35,11 @@ - + - + From 0f69cb7195151555f425d395d3dd171022a8f43e Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 12:30:07 -0400 Subject: [PATCH 09/32] Added comment to remind myself to remove legacy code in WebhookParser.cs when we drop support for net461 --- Source/StrongGrid/WebhookParser.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/StrongGrid/WebhookParser.cs b/Source/StrongGrid/WebhookParser.cs index 516386bc..e69c23ab 100644 --- a/Source/StrongGrid/WebhookParser.cs +++ b/Source/StrongGrid/WebhookParser.cs @@ -109,6 +109,9 @@ public Event[] ParseSignedEventsWebhook(string requestBody, string publicKey, st The 'ECDsa.ImportSubjectPublicKeyInfo' method was introduced in .NET core 3.0 and the DSASignatureFormat enum was introduced in .NET 5.0. + We can get rid of the code that relies on ECDsaCng and remove reference to + System.Security.Cryptography.Cng in csproj when we drop support for net461. + We can get rid of the 'ConvertECDSASignature' class and the Utils methods that convert public keys when we stop suporting .NET framework and .NET standard From dfe07c7713c74eb1eed75add850830d48b3b6adc Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 12:32:57 -0400 Subject: [PATCH 10/32] Target net5.0 Resolves #378 --- .../StrongGrid.IntegrationTests.csproj | 2 +- Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj | 2 +- Source/StrongGrid/StrongGrid.csproj | 2 +- build.cake | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj b/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj index f1236244..e5c903a6 100644 --- a/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj +++ b/Source/StrongGrid.IntegrationTests/StrongGrid.IntegrationTests.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 StrongGrid.IntegrationTests StrongGrid.IntegrationTests diff --git a/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj b/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj index 0c553b38..f7be1be2 100644 --- a/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj +++ b/Source/StrongGrid.UnitTests/StrongGrid.UnitTests.csproj @@ -1,7 +1,7 @@ - net461;net472;netcoreapp3.1 + net461;net472;net5.0 StrongGrid.UnitTests StrongGrid.UnitTests diff --git a/Source/StrongGrid/StrongGrid.csproj b/Source/StrongGrid/StrongGrid.csproj index 524366a5..6924d226 100644 --- a/Source/StrongGrid/StrongGrid.csproj +++ b/Source/StrongGrid/StrongGrid.csproj @@ -1,7 +1,7 @@ - net461;net472;netstandard2.0 + net461;net472;netstandard2.0;net5.0 anycpu true Library diff --git a/build.cake b/build.cake index 1bdc5bf6..0e6aa7b6 100644 --- a/build.cake +++ b/build.cake @@ -195,7 +195,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" + Framework = IsRunningOnWindows() ? null : "net5.0" }); }); From c0c7aea22beb3c29f1247dc051027926ab544ccc Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 12:46:09 -0400 Subject: [PATCH 11/32] Fix error CA2214: Do not call overridable methods in constructors --- Source/StrongGrid/BaseClient.cs | 8 +------- Source/StrongGrid/Client.cs | 31 ++++++++++--------------------- Source/StrongGrid/LegacyClient.cs | 31 ++++++++++--------------------- 3 files changed, 21 insertions(+), 49 deletions(-) diff --git a/Source/StrongGrid/BaseClient.cs b/Source/StrongGrid/BaseClient.cs index 5e435df9..887b960a 100644 --- a/Source/StrongGrid/BaseClient.cs +++ b/Source/StrongGrid/BaseClient.cs @@ -277,7 +277,7 @@ public BaseClient(string apiKey, HttpClient httpClient, bool disposeClient, Stro { _mustDisposeHttpClient = disposeClient; _httpClient = httpClient; - _options = options ?? GetDefaultOptions(); + _options = options; _logger = logger ?? NullLogger.Instance; _fluentClient = new FluentClient(new Uri(SENDGRID_V3_BASE_URI), httpClient) @@ -337,12 +337,6 @@ public BaseClient(string apiKey, HttpClient httpClient, bool disposeClient, Stro #region PUBLIC METHODS - /// - /// When overridden in a derived class, returns the default options. - /// - /// The default options. - public abstract StrongGridClientOptions GetDefaultOptions(); - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/Source/StrongGrid/Client.cs b/Source/StrongGrid/Client.cs index b8465377..2205547b 100644 --- a/Source/StrongGrid/Client.cs +++ b/Source/StrongGrid/Client.cs @@ -11,6 +11,12 @@ namespace StrongGrid /// public class Client : BaseClient, IClient { + private static readonly StrongGridClientOptions _defaultOptions = new StrongGridClientOptions() + { + LogLevelSuccessfulCalls = LogLevel.Debug, + LogLevelFailedCalls = LogLevel.Error + }; + #region PROPERTIES /// @@ -72,7 +78,7 @@ public class Client : BaseClient, IClient /// Options for the SendGrid client. /// Logger. public Client(string apiKey, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, null, false, options, logger) + : base(apiKey, null, false, options ?? _defaultOptions, logger) { Init(); } @@ -85,7 +91,7 @@ public Client(string apiKey, StrongGridClientOptions options = null, ILogger log /// Options for the SendGrid client. /// Logger. public Client(string apiKey, IWebProxy proxy, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, new HttpClient(new HttpClientHandler { Proxy = proxy, UseProxy = proxy != null }), true, options, logger) + : base(apiKey, new HttpClient(new HttpClientHandler { Proxy = proxy, UseProxy = proxy != null }), true, options ?? _defaultOptions, logger) { Init(); } @@ -98,7 +104,7 @@ public Client(string apiKey, IWebProxy proxy, StrongGridClientOptions options = /// Options for the SendGrid client. /// Logger. public Client(string apiKey, HttpMessageHandler handler, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, new HttpClient(handler), true, options, logger) + : base(apiKey, new HttpClient(handler), true, options ?? _defaultOptions, logger) { Init(); } @@ -111,30 +117,13 @@ public Client(string apiKey, HttpMessageHandler handler, StrongGridClientOptions /// Options for the SendGrid client. /// Logger. public Client(string apiKey, HttpClient httpClient, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, httpClient, false, options, logger) + : base(apiKey, httpClient, false, options ?? _defaultOptions, logger) { Init(); } #endregion - #region PUBLIC METHODS - - /// - /// Return the default options. - /// - /// The default options. - public override StrongGridClientOptions GetDefaultOptions() - { - return new StrongGridClientOptions() - { - LogLevelSuccessfulCalls = LogLevel.Debug, - LogLevelFailedCalls = LogLevel.Error - }; - } - - #endregion - #region PRIVATE METHODS private void Init() diff --git a/Source/StrongGrid/LegacyClient.cs b/Source/StrongGrid/LegacyClient.cs index 855e8091..f30b4dfb 100644 --- a/Source/StrongGrid/LegacyClient.cs +++ b/Source/StrongGrid/LegacyClient.cs @@ -10,6 +10,12 @@ namespace StrongGrid /// public class LegacyClient : BaseClient, ILegacyClient { + private static readonly StrongGridClientOptions _defaultOptions = new StrongGridClientOptions() + { + LogLevelSuccessfulCalls = LogLevel.Debug, + LogLevelFailedCalls = LogLevel.Debug + }; + #region PROPERTIES /// @@ -79,7 +85,7 @@ public class LegacyClient : BaseClient, ILegacyClient /// Options for the SendGrid client. /// Logger. public LegacyClient(string apiKey, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, null, false, options, logger) + : base(apiKey, null, false, options ?? _defaultOptions, logger) { Init(); } @@ -92,7 +98,7 @@ public LegacyClient(string apiKey, StrongGridClientOptions options = null, ILogg /// Options for the SendGrid client. /// Logger. public LegacyClient(string apiKey, IWebProxy proxy, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, new HttpClient(new HttpClientHandler { Proxy = proxy, UseProxy = proxy != null }), true, options, logger) + : base(apiKey, new HttpClient(new HttpClientHandler { Proxy = proxy, UseProxy = proxy != null }), true, options ?? _defaultOptions, logger) { Init(); } @@ -105,7 +111,7 @@ public LegacyClient(string apiKey, IWebProxy proxy, StrongGridClientOptions opti /// Options for the SendGrid client. /// Logger. public LegacyClient(string apiKey, HttpMessageHandler handler, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, new HttpClient(handler), true, options, logger) + : base(apiKey, new HttpClient(handler), true, options ?? _defaultOptions, logger) { Init(); } @@ -118,30 +124,13 @@ public LegacyClient(string apiKey, HttpMessageHandler handler, StrongGridClientO /// Options for the SendGrid client. /// Logger. public LegacyClient(string apiKey, HttpClient httpClient, StrongGridClientOptions options = null, ILogger logger = null) - : base(apiKey, httpClient, false, options, logger) + : base(apiKey, httpClient, false, options ?? _defaultOptions, logger) { Init(); } #endregion - #region PUBLIC METHODS - - /// - /// Return the default options. - /// - /// The default options. - public override StrongGridClientOptions GetDefaultOptions() - { - return new StrongGridClientOptions() - { - LogLevelSuccessfulCalls = LogLevel.Debug, - LogLevelFailedCalls = LogLevel.Debug - }; - } - - #endregion - #region PRIVATE METHODS private void Init() From f7d3c7f80f9432ddfda21da92fd1a0349d9974ee Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 12:46:59 -0400 Subject: [PATCH 12/32] Fix compiler errors --- Source/StrongGrid/StrongGrid.csproj | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Source/StrongGrid/StrongGrid.csproj b/Source/StrongGrid/StrongGrid.csproj index 6924d226..1f0ab2ac 100644 --- a/Source/StrongGrid/StrongGrid.csproj +++ b/Source/StrongGrid/StrongGrid.csproj @@ -45,7 +45,7 @@ - + @@ -57,14 +57,6 @@ - - $(DefineConstants);NETFULL - - - - $(DefineConstants);NETSTANDARD - - true From 5fa04619bea17b3395247c76b2f1e44c90dfb137 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 12:49:35 -0400 Subject: [PATCH 13/32] Fix error CA1014: Mark assemblies with CLSCompliant --- Source/StrongGrid/Properties/AssemblyInfo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/StrongGrid/Properties/AssemblyInfo.cs b/Source/StrongGrid/Properties/AssemblyInfo.cs index 0480d67b..faac79e3 100644 --- a/Source/StrongGrid/Properties/AssemblyInfo.cs +++ b/Source/StrongGrid/Properties/AssemblyInfo.cs @@ -1,3 +1,4 @@ +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -9,3 +10,5 @@ // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] + +[assembly: CLSCompliant(true)] From 0a59c858e179512d747f8b5fd38210905b6ce5d6 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 12:50:31 -0400 Subject: [PATCH 14/32] Run unit tests under both netcoreapp3.1 and net5.0 on Ubuntu --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 0e6aa7b6..90db54b7 100644 --- a/build.cake +++ b/build.cake @@ -195,7 +195,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "net5.0" + Framework = IsRunningOnWindows() ? null : "netcoreapp3.1;net5.0" }); }); From 77b138388f070c3275ded0f9dde103dc88c1e793 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 13:11:16 -0400 Subject: [PATCH 15/32] Fix buid issue on Ubuntu --- build.cake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 90db54b7..d6ff32a3 100644 --- a/build.cake +++ b/build.cake @@ -182,7 +182,8 @@ Task("Build") { Configuration = configuration, NoRestore = true, - ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded) + ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), + Framework = IsRunningOnWindows() ? null : "netstandard2.0;net5.0" }); }); From 4c11aed1473b4dab35b9f813dbd9b06e56686682 Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 13:16:25 -0400 Subject: [PATCH 16/32] Fix buid issue on Ubuntu --- build.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index d6ff32a3..18aeba1a 100644 --- a/build.cake +++ b/build.cake @@ -183,7 +183,7 @@ Task("Build") Configuration = configuration, NoRestore = true, ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "netstandard2.0;net5.0" + Framework = IsRunningOnWindows() ? null : "netstandard2.0" }); }); @@ -196,7 +196,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "netcoreapp3.1;net5.0" + Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" }); }); From e90af910d9fe86d5217244c0ca034d147756f55f Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 14:00:33 -0400 Subject: [PATCH 17/32] Attempt to fix build issue on Ubuntu --- build.cake | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/build.cake b/build.cake index 18aeba1a..efd88c25 100644 --- a/build.cake +++ b/build.cake @@ -49,6 +49,9 @@ var outputDir = "./artifacts/"; var codeCoverageDir = $"{outputDir}CodeCoverage/"; var benchmarkDir = $"{outputDir}Benchmark/"; +var solutionFile = $"{sourceFolder}{libraryName}.sln"; +var sourceProject = $"{sourceFolder}{libraryName}/{libraryName}.csproj"; +var integrationTestsProject = $"{sourceFolder}{libraryName}.IntegrationTests/{libraryName}.IntegrationTests.csproj"; var unitTestsProject = $"{sourceFolder}{libraryName}.UnitTests/{libraryName}.UnitTests.csproj"; var benchmarkProject = $"{sourceFolder}{libraryName}.Benchmark/{libraryName}.Benchmark.csproj"; @@ -178,12 +181,12 @@ Task("Build") .IsDependentOn("Restore-NuGet-Packages") .Does(() => { - DotNetCoreBuild($"{sourceFolder}{libraryName}.sln", new DotNetCoreBuildSettings + DotNetCoreBuild(solutionFile, new DotNetCoreBuildSettings { Configuration = configuration, NoRestore = true, ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "netstandard2.0" + Framework = IsRunningOnWindows() ? null : "net5.0" }); }); @@ -196,7 +199,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" + Framework = IsRunningOnWindows() ? null : "net5.0" }); }); @@ -272,7 +275,7 @@ Task("Create-NuGet-Package") } }; - DotNetCorePack($"{sourceFolder}{libraryName}/{libraryName}.csproj", settings); + DotNetCorePack(sourceProject, settings); }); Task("Upload-AppVeyor-Artifacts") From aa741b8d635a9dbdfedd299bb1341afeef13691f Mon Sep 17 00:00:00 2001 From: Jericho Date: Tue, 13 Apr 2021 14:11:17 -0400 Subject: [PATCH 18/32] Attempt to fix build issue on Ubuntu --- Source/StrongGrid/StrongGrid.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/StrongGrid/StrongGrid.csproj b/Source/StrongGrid/StrongGrid.csproj index 1f0ab2ac..6203983d 100644 --- a/Source/StrongGrid/StrongGrid.csproj +++ b/Source/StrongGrid/StrongGrid.csproj @@ -57,6 +57,14 @@ + + $(DefineConstants);NETFULL + + + + $(DefineConstants);NETSTANDARD + + true From 0f0a10a783a3921d703ef20d436ad406ae68551a Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 10:36:32 -0400 Subject: [PATCH 19/32] Ignore integration tests when running on AppVeyor --- build.cake | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index efd88c25..b7161ed1 100644 --- a/build.cake +++ b/build.cake @@ -165,8 +165,27 @@ Task("Clean") CreateDirectory(codeCoverageDir); }); +Task("Remove-Integration-Tests") + .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor) + .Does(() => +{ + // Integration tests are intended to be used for debugging purposes and not intended to be executed in CI environment. + // Also, the runner for these tests contains windows-specific code (such as resizing window, moving window to center of screen, etc.) + // which cause problems when attempting to run unit tests on an Ubuntu image on AppVeyor. + + Information("Here are the project in the solution before removing integration tests:"); + DotNetCoreTool(solutionFile, "sln", "list"); + Information(""); + + DotNetCoreTool(solutionFile, "sln", $"remove {integrationTestsProject.TrimStart(sourceFolder, StringComparison.OrdinalIgnoreCase)}"); + Information(""); + + Information("Here are the project in the solution after removing integration tests:"); + DotNetCoreTool(solutionFile, "sln", "list"); +}); + Task("Restore-NuGet-Packages") - .IsDependentOn("Clean") + .IsDependentOn("Remove-Integration-Tests") .Does(() => { DotNetCoreRestore("./Source/", new DotNetCoreRestoreSettings @@ -450,3 +469,25 @@ Task("Default") /////////////////////////////////////////////////////////////////////////////// RunTarget(target); + + + +/////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS +/////////////////////////////////////////////////////////////////////////////// +private static string TrimStart(this string source, string value, StringComparison comparisonType) +{ + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + int valueLength = value.Length; + int startIndex = 0; + while (source.IndexOf(value, startIndex, comparisonType) == startIndex) + { + startIndex += valueLength; + } + + return source.Substring(startIndex); +} From 226e92a8d59ae019c20e4ceb1313b31aca070a60 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 10:43:41 -0400 Subject: [PATCH 20/32] Refresh resource files --- .gitignore | 4 ++++ build.cake | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 537870fd..4bef1e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -363,6 +363,10 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd +### VisualStudio Patch ### +# Additional files built by Visual Studio +*.tlog + # WinMerge *.bak diff --git a/build.cake b/build.cake index b7161ed1..e2ebaaa1 100644 --- a/build.cake +++ b/build.cake @@ -7,7 +7,7 @@ #tool nuget:?package=xunit.runner.console&version=2.4.1 // Install addins. -#addin nuget:?package=Cake.Coveralls&version=1.0.0 +#addin nuget:?package=Cake.Coveralls&version=1.0.1 /////////////////////////////////////////////////////////////////////////////// @@ -147,8 +147,28 @@ Task("AppVeyor-Build_Number") }); }); +Task("Remove-Integration-Tests") + .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor) + .Does(() => +{ + // Integration tests are intended to be used for debugging purposes and not intended to be executed in CI environment. + // Also, the runner for these tests contains windows-specific code (such as resizing window, moving window to center of screen, etc.) + // which can cause problems when attempting to run unit tests on an Ubuntu image on AppVeyor. + + Information("Here are the projects in the solution before removing integration tests:"); + DotNetCoreTool(solutionFile, "sln", "list"); + Information(""); + + DotNetCoreTool(solutionFile, "sln", $"remove {integrationTestsProject.TrimStart(sourceFolder, StringComparison.OrdinalIgnoreCase)}"); + Information(""); + + Information("Here are the projects in the solution after removing integration tests:"); + DotNetCoreTool(solutionFile, "sln", "list"); +}); + Task("Clean") .IsDependentOn("AppVeyor-Build_Number") + .IsDependentOn("Remove-Integration-Tests") .Does(() => { // Clean solution directories. @@ -165,27 +185,8 @@ Task("Clean") CreateDirectory(codeCoverageDir); }); -Task("Remove-Integration-Tests") - .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor) - .Does(() => -{ - // Integration tests are intended to be used for debugging purposes and not intended to be executed in CI environment. - // Also, the runner for these tests contains windows-specific code (such as resizing window, moving window to center of screen, etc.) - // which cause problems when attempting to run unit tests on an Ubuntu image on AppVeyor. - - Information("Here are the project in the solution before removing integration tests:"); - DotNetCoreTool(solutionFile, "sln", "list"); - Information(""); - - DotNetCoreTool(solutionFile, "sln", $"remove {integrationTestsProject.TrimStart(sourceFolder, StringComparison.OrdinalIgnoreCase)}"); - Information(""); - - Information("Here are the project in the solution after removing integration tests:"); - DotNetCoreTool(solutionFile, "sln", "list"); -}); - Task("Restore-NuGet-Packages") - .IsDependentOn("Remove-Integration-Tests") + .IsDependentOn("Clean") .Does(() => { DotNetCoreRestore("./Source/", new DotNetCoreRestoreSettings @@ -205,7 +206,7 @@ Task("Build") Configuration = configuration, NoRestore = true, ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "net5.0" + Framework = IsRunningOnWindows() ? null : "netstandar2.0;net5.0" }); }); @@ -218,7 +219,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "net5.0" + Framework = IsRunningOnWindows() ? null : "netcoreapp3.1;net5.0" }); }); From 94f609733d8441fbaf6a9ab3946ecd4cf57aab01 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 10:51:47 -0400 Subject: [PATCH 21/32] Only build netstandard2.0 on Ubuntu --- build.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index e2ebaaa1..30ab8852 100644 --- a/build.cake +++ b/build.cake @@ -206,7 +206,7 @@ Task("Build") Configuration = configuration, NoRestore = true, ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "netstandar2.0;net5.0" + Framework = IsRunningOnWindows() ? null : "netstandar2.0" }); }); @@ -219,7 +219,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "netcoreapp3.1;net5.0" + Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" }); }); From 4cb6d2b57983c5fc10f3e48b442335f855c30a07 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 10:59:25 -0400 Subject: [PATCH 22/32] Debugging for Ubuntu --- appveyor.yml | 1 - build.cake | 34 ++++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8c9b260c..f2f7c997 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,6 @@ cache: # Environment configuration image: - - Visual Studio 2019 - Ubuntu1804 #---------------------------------# diff --git a/build.cake b/build.cake index 30ab8852..a96dd43d 100644 --- a/build.cake +++ b/build.cake @@ -201,26 +201,32 @@ Task("Build") .IsDependentOn("Restore-NuGet-Packages") .Does(() => { - DotNetCoreBuild(solutionFile, new DotNetCoreBuildSettings - { - Configuration = configuration, - NoRestore = true, - ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "netstandar2.0" - }); + using (DiagnosticVerbosity()) + { + DotNetCoreBuild(solutionFile, new DotNetCoreBuildSettings + { + Configuration = configuration, + NoRestore = true, + ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), + Framework = IsRunningOnWindows() ? null : "netstandar2.0" + }); + } }); Task("Run-Unit-Tests") .IsDependentOn("Build") .Does(() => { - DotNetCoreTest(unitTestsProject, new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" - }); + using (DiagnosticVerbosity()) + { + DotNetCoreTest(unitTestsProject, new DotNetCoreTestSettings + { + NoBuild = true, + NoRestore = true, + Configuration = configuration, + Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" + }); + } }); Task("Run-Code-Coverage") From c2f2275894db94502155c7f762f9551cfbc7895d Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:01:36 -0400 Subject: [PATCH 23/32] Fix typo --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index a96dd43d..db030138 100644 --- a/build.cake +++ b/build.cake @@ -208,7 +208,7 @@ Task("Build") Configuration = configuration, NoRestore = true, ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "netstandar2.0" + Framework = IsRunningOnWindows() ? null : "netstandard2.0" }); } }); From bdb3cc399919cd5e50fd8adc5831c6540b87aa50 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:08:01 -0400 Subject: [PATCH 24/32] Build net5.0 target on Ubuntu and also run net5.0 unit tests --- build.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index db030138..c577a842 100644 --- a/build.cake +++ b/build.cake @@ -208,7 +208,7 @@ Task("Build") Configuration = configuration, NoRestore = true, ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "netstandard2.0" + Framework = IsRunningOnWindows() ? null : "net5.0" }); } }); @@ -224,7 +224,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "netcoreapp3.1" + Framework = IsRunningOnWindows() ? null : "net5.0" }); } }); From 45afcbf80c0a2e22401f5e86cd5bce15d9b3f88d Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:20:09 -0400 Subject: [PATCH 25/32] Debugging code --- Source/StrongGrid/WebhookParser.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/StrongGrid/WebhookParser.cs b/Source/StrongGrid/WebhookParser.cs index e69c23ab..b9fa7fae 100644 --- a/Source/StrongGrid/WebhookParser.cs +++ b/Source/StrongGrid/WebhookParser.cs @@ -36,7 +36,7 @@ public class WebhookParser #region CTOR -#if NETSTANDARD +#if NETSTANDARD2_0 || NET5_0_OR_GREATER static WebhookParser() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); @@ -142,7 +142,7 @@ ECDsaCng is Windows only. } }); var verified = eCDsa.VerifyData(data, sig, HashAlgorithmName.SHA256); -#else +#elif NETFRAMEWORK // Convert the signature and public key provided by SendGrid into formats usable by the ECDsaCng .net crypto class var sig = ConvertECDSASignature.LightweightConvertSignatureFromX9_62ToISO7816_8(256, signatureBytes); var cngBlob = Utils.ConvertSecp256R1PublicKeyToEccPublicBlob(publicKeyBytes); @@ -151,6 +151,8 @@ ECDsaCng is Windows only. var cngKey = CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob); var eCDsaCng = new ECDsaCng(cngKey); var verified = eCDsaCng.VerifyData(data, sig); +#else + throw new Exception("CONFUSION: what framework is this????"); #endif if (!verified) From 03fbca51e167f4d3608bbc9dd1dde6feb8e219cd Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:28:33 -0400 Subject: [PATCH 26/32] Trying to figure out why 'NET5_0_OR_GREATER' doesn't work on Ubuntu --- Source/StrongGrid/WebhookParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/StrongGrid/WebhookParser.cs b/Source/StrongGrid/WebhookParser.cs index b9fa7fae..9d5b130c 100644 --- a/Source/StrongGrid/WebhookParser.cs +++ b/Source/StrongGrid/WebhookParser.cs @@ -120,7 +120,7 @@ ECDsa is cross-platform and can be used on Windows and Linux/Ubuntu. ECDsaCng is Windows only. */ -#if NET5_0_OR_GREATER +#if NET5_0 || NET5_0_OR_GREATER // Verify the signature var eCDsa = ECDsa.Create(); eCDsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _); @@ -152,7 +152,7 @@ ECDsaCng is Windows only. var eCDsaCng = new ECDsaCng(cngKey); var verified = eCDsaCng.VerifyData(data, sig); #else - throw new Exception("CONFUSION: what framework is this????"); +#error Unhandled TFM #endif if (!verified) From 08c512b529cbf018f2cb2c63ad088accc512e1ab Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:35:36 -0400 Subject: [PATCH 27/32] Debugging --- Source/StrongGrid/WebhookParser.cs | 2 +- build.cake | 39 ++++++++++++++---------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Source/StrongGrid/WebhookParser.cs b/Source/StrongGrid/WebhookParser.cs index 9d5b130c..aa45554e 100644 --- a/Source/StrongGrid/WebhookParser.cs +++ b/Source/StrongGrid/WebhookParser.cs @@ -120,7 +120,7 @@ ECDsa is cross-platform and can be used on Windows and Linux/Ubuntu. ECDsaCng is Windows only. */ -#if NET5_0 || NET5_0_OR_GREATER +#if NET5_0_OR_GREATER // Verify the signature var eCDsa = ECDsa.Create(); eCDsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _); diff --git a/build.cake b/build.cake index c577a842..405a3c0b 100644 --- a/build.cake +++ b/build.cake @@ -201,32 +201,26 @@ Task("Build") .IsDependentOn("Restore-NuGet-Packages") .Does(() => { - using (DiagnosticVerbosity()) - { - DotNetCoreBuild(solutionFile, new DotNetCoreBuildSettings - { - Configuration = configuration, - NoRestore = true, - ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), - Framework = IsRunningOnWindows() ? null : "net5.0" - }); - } + DotNetCoreBuild(solutionFile, new DotNetCoreBuildSettings + { + Configuration = configuration, + NoRestore = true, + ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.LegacySemVerPadded), + Framework = IsRunningOnWindows() ? null : "net5.0" + }); }); Task("Run-Unit-Tests") .IsDependentOn("Build") .Does(() => { - using (DiagnosticVerbosity()) - { - DotNetCoreTest(unitTestsProject, new DotNetCoreTestSettings - { - NoBuild = true, - NoRestore = true, - Configuration = configuration, - Framework = IsRunningOnWindows() ? null : "net5.0" - }); - } + DotNetCoreTest(unitTestsProject, new DotNetCoreTestSettings + { + NoBuild = true, + NoRestore = true, + Configuration = configuration, + Framework = IsRunningOnWindows() ? null : "net5.0" + }); }); Task("Run-Code-Coverage") @@ -301,7 +295,10 @@ Task("Create-NuGet-Package") } }; - DotNetCorePack(sourceProject, settings); + using (DiagnosticVerbosity()) + { + DotNetCorePack(sourceProject, settings); + } }); Task("Upload-AppVeyor-Artifacts") From 16892bfc23f91dc3f21d179f29b5a585b66eaedc Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:41:38 -0400 Subject: [PATCH 28/32] Debugging --- Source/StrongGrid/WebhookParser.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/StrongGrid/WebhookParser.cs b/Source/StrongGrid/WebhookParser.cs index aa45554e..1a27cd29 100644 --- a/Source/StrongGrid/WebhookParser.cs +++ b/Source/StrongGrid/WebhookParser.cs @@ -113,14 +113,17 @@ We can get rid of the code that relies on ECDsaCng and remove reference to System.Security.Cryptography.Cng in csproj when we drop support for net461. We can get rid of the 'ConvertECDSASignature' class and the Utils methods that - convert public keys when we stop suporting .NET framework and .NET standard + convert public keys when we stop suporting .NET framework and .NET standard. + + NET5_0_OR_GREATER works fine on Windows but doesn't work on Ubuntu. That's I + added the seemingly redundant "NET5_0" in the conditional block below. Note: ECDsa is cross-platform and can be used on Windows and Linux/Ubuntu. ECDsaCng is Windows only. */ -#if NET5_0_OR_GREATER +#if NET5_0 || NET5_0_OR_GREATER // Verify the signature var eCDsa = ECDsa.Create(); eCDsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _); From 6b8899b475e78d1ed01ceb0cc075e3442dff08fc Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 11:52:13 -0400 Subject: [PATCH 29/32] Do not create NuGet package on Ubuntu --- build.cake | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build.cake b/build.cake index 405a3c0b..773662a9 100644 --- a/build.cake +++ b/build.cake @@ -295,10 +295,7 @@ Task("Create-NuGet-Package") } }; - using (DiagnosticVerbosity()) - { - DotNetCorePack(sourceProject, settings); - } + DotNetCorePack(sourceProject, settings); }); Task("Upload-AppVeyor-Artifacts") @@ -460,8 +457,6 @@ Task("AppVeyor") Task("AppVeyor-Ubuntu") .IsDependentOn("Run-Unit-Tests") - .IsDependentOn("Create-NuGet-Package") - .IsDependentOn("Upload-AppVeyor-Artifacts"); Task("Default") .IsDependentOn("Run-Unit-Tests") From e21eeaebf514d77df443022a5c3b094cda621cd3 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 12:22:36 -0400 Subject: [PATCH 30/32] Update build script --- Source/StrongGrid/StrongGrid.csproj | 8 -------- build.cake | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Source/StrongGrid/StrongGrid.csproj b/Source/StrongGrid/StrongGrid.csproj index 6203983d..3657ffb8 100644 --- a/Source/StrongGrid/StrongGrid.csproj +++ b/Source/StrongGrid/StrongGrid.csproj @@ -56,14 +56,6 @@ - - - $(DefineConstants);NETFULL - - - - $(DefineConstants);NETSTANDARD - true diff --git a/build.cake b/build.cake index 773662a9..86ab4c18 100644 --- a/build.cake +++ b/build.cake @@ -456,7 +456,7 @@ Task("AppVeyor") .IsDependentOn("Publish-GitHub-Release"); Task("AppVeyor-Ubuntu") - .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Run-Unit-Tests"); Task("Default") .IsDependentOn("Run-Unit-Tests") From 2986b2ac4c6ce5d102f5acde3fa25445635b67f7 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 12:29:03 -0400 Subject: [PATCH 31/32] Debugging on Ubuntu completed. Therefore restoring Windows --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index f2f7c997..8c9b260c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,6 +25,7 @@ cache: # Environment configuration image: + - Visual Studio 2019 - Ubuntu1804 #---------------------------------# From 0af4aeab348008865b23552f0e7cebcd1648584e Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 14 Apr 2021 12:31:43 -0400 Subject: [PATCH 32/32] Improve comment to explain why the seemingly redundant "NET5_0" is necessary and when it will be safe to remove it --- Source/StrongGrid/WebhookParser.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/StrongGrid/WebhookParser.cs b/Source/StrongGrid/WebhookParser.cs index 1a27cd29..9699f4da 100644 --- a/Source/StrongGrid/WebhookParser.cs +++ b/Source/StrongGrid/WebhookParser.cs @@ -115,8 +115,11 @@ We can get rid of the code that relies on ECDsaCng and remove reference to We can get rid of the 'ConvertECDSASignature' class and the Utils methods that convert public keys when we stop suporting .NET framework and .NET standard. - NET5_0_OR_GREATER works fine on Windows but doesn't work on Ubuntu. That's I - added the seemingly redundant "NET5_0" in the conditional block below. + NET5_0_OR_GREATER was added to the NET SDK very recently and works fine on + AppVeyor's Windows image which runs NET SDK 5.0.201. However, it doesn't work + on their Ubuntu image because it's still on NET SDK 5.0.101. That's why I added + the seemingly redundant "NET5_0" in the conditional block below. It will be safe + to remove it when the SDK in AppVeyor's Ubuntu image is upgraded. Note: ECDsa is cross-platform and can be used on Windows and Linux/Ubuntu.