diff --git a/Source/CakeMail.RestClient.IntegrationTests/CakeMail.RestClient.IntegrationTests.csproj b/Source/CakeMail.RestClient.IntegrationTests/CakeMail.RestClient.IntegrationTests.csproj
index 009c51e..66703d2 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/CakeMail.RestClient.IntegrationTests.csproj
+++ b/Source/CakeMail.RestClient.IntegrationTests/CakeMail.RestClient.IntegrationTests.csproj
@@ -11,6 +11,10 @@
latest
+
+
+
+
diff --git a/Source/CakeMail.RestClient.IntegrationTests/ConsoleUtils.cs b/Source/CakeMail.RestClient.IntegrationTests/ConsoleUtils.cs
new file mode 100644
index 0000000..966c9e0
--- /dev/null
+++ b/Source/CakeMail.RestClient.IntegrationTests/ConsoleUtils.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace CakeMail.RestClient.IntegrationTests
+{
+ public static class ConsoleUtils
+ {
+ public static void CenterConsole()
+ {
+ var hWin = NativeMethods.GetConsoleWindow();
+ if (hWin == IntPtr.Zero) return;
+
+ var monitor = NativeMethods.MonitorFromWindow(hWin, NativeMethods.MONITOR_DEFAULT_TO_NEAREST);
+ if (monitor == IntPtr.Zero) return;
+
+ var monitorInfo = new NativeMethods.NativeMonitorInfo();
+ NativeMethods.GetMonitorInfo(monitor, monitorInfo);
+
+ NativeMethods.GetWindowRect(hWin, out NativeMethods.NativeRectangle consoleInfo);
+
+ var monitorWidth = monitorInfo.Monitor.Right - monitorInfo.Monitor.Left;
+ var monitorHeight = monitorInfo.Monitor.Bottom - monitorInfo.Monitor.Top;
+
+ var consoleWidth = consoleInfo.Right - consoleInfo.Left;
+ var consoleHeight = consoleInfo.Bottom - consoleInfo.Top;
+
+ var left = monitorInfo.Monitor.Left + ((monitorWidth - consoleWidth) / 2);
+ var top = monitorInfo.Monitor.Top + ((monitorHeight - consoleHeight) / 2);
+
+ NativeMethods.MoveWindow(hWin, left, top, consoleWidth, consoleHeight, false);
+ }
+ }
+}
diff --git a/Source/CakeMail.RestClient.IntegrationTests/IIntegrationTest.cs b/Source/CakeMail.RestClient.IntegrationTests/IIntegrationTest.cs
new file mode 100644
index 0000000..c797cd9
--- /dev/null
+++ b/Source/CakeMail.RestClient.IntegrationTests/IIntegrationTest.cs
@@ -0,0 +1,11 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace CakeMail.RestClient.IntegrationTests
+{
+ public interface IIntegrationTest
+ {
+ Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken);
+ }
+}
diff --git a/Source/CakeMail.RestClient.IntegrationTests/NativeMethods.cs b/Source/CakeMail.RestClient.IntegrationTests/NativeMethods.cs
new file mode 100644
index 0000000..1fe8718
--- /dev/null
+++ b/Source/CakeMail.RestClient.IntegrationTests/NativeMethods.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace CakeMail.RestClient.IntegrationTests
+{
+ public static class NativeMethods
+ {
+ public const Int32 MONITOR_DEFAULT_TO_PRIMARY = 0x00000001;
+ public const Int32 MONITOR_DEFAULT_TO_NEAREST = 0x00000002;
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr GetConsoleWindow();
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool GetWindowRect(IntPtr hWnd, out NativeRectangle rc);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr MonitorFromWindow(IntPtr handle, Int32 flags);
+
+ [DllImport("user32.dll")]
+ public static extern Boolean GetMonitorInfo(IntPtr hMonitor, NativeMonitorInfo lpmi);
+
+ [Serializable, StructLayout(LayoutKind.Sequential)]
+ public struct NativeRectangle
+ {
+ public Int32 Left;
+ public Int32 Top;
+ public Int32 Right;
+ public Int32 Bottom;
+
+ public NativeRectangle(Int32 left, Int32 top, Int32 right, Int32 bottom)
+ {
+ this.Left = left;
+ this.Top = top;
+ this.Right = right;
+ this.Bottom = bottom;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public sealed class NativeMonitorInfo
+ {
+ public Int32 Size = Marshal.SizeOf(typeof(NativeMonitorInfo));
+ public NativeRectangle Monitor;
+ public NativeRectangle Work;
+ public Int32 Flags;
+ }
+ }
+}
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Program.cs b/Source/CakeMail.RestClient.IntegrationTests/Program.cs
index 5665c82..ff9306b 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Program.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Program.cs
@@ -1,7 +1,12 @@
using CakeMail.RestClient.IntegrationTests.Tests;
-using CakeMail.RestClient.Logging;
+using CakeMail.RestClient.Utilities;
+using Logzio.DotNet.NLog;
+using NLog;
+using NLog.Config;
+using NLog.Targets;
using System;
using System.IO;
+using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
@@ -10,30 +15,63 @@ namespace CakeMail.RestClient.IntegrationTests
{
public class Program
{
+ private const int MAX_CAKEMAIL_API_CONCURRENCY = 5;
+
+ private enum ResultCodes
+ {
+ Success = 0,
+ Exception = 1,
+ Cancelled = 1223
+ }
+
static async Task Main()
{
// -----------------------------------------------------------------------------
-
- // Do you want to proxy requests through Fiddler (useful for debugging)?
+ // Do you want to proxy requests through Fiddler? Can be useful for debugging.
var useFiddler = false;
- // As an alternative to Fiddler, you can display debug information about
- // every HTTP request/response in the console. This is useful for debugging
- // purposes but the amount of information can be overwhelming.
- var debugHttpMessagesToConsole = false;
+ // Logging options.
+ var options = new CakeMailClientOptions()
+ {
+ LogLevelFailedCalls = CakeMail.RestClient.Logging.LogLevel.Error,
+ LogLevelSuccessfulCalls = CakeMail.RestClient.Logging.LogLevel.Debug
+ };
// -----------------------------------------------------------------------------
- var proxy = useFiddler ? new WebProxy("http://localhost:8888") : null;
+ // Configure logging
+ var nLogConfig = new LoggingConfiguration();
+
+ // Send logs to logz.io
+ var logzioToken = Environment.GetEnvironmentVariable("LOGZIO_TOKEN");
+ if (!string.IsNullOrEmpty(logzioToken))
+ {
+ var logzioTarget = new LogzioTarget { Token = logzioToken };
+ logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("source", "StrongGrid_integration_tests"));
+ logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("StrongGrid-Version", CakeMailRestClient.Version));
+
+ nLogConfig.AddTarget("Logzio", logzioTarget);
+ nLogConfig.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, "Logzio", "*");
+ }
+
+ // Send logs to console
+ var consoleTarget = new ColoredConsoleTarget();
+ nLogConfig.AddTarget("ColoredConsole", consoleTarget);
+ nLogConfig.AddRule(NLog.LogLevel.Warn, NLog.LogLevel.Fatal, "ColoredConsole", "*");
+
+ LogManager.Configuration = nLogConfig;
+
+ // Configure CakeMail client
var apiKey = Environment.GetEnvironmentVariable("CAKEMAIL_APIKEY");
var userName = Environment.GetEnvironmentVariable("CAKEMAIL_USERNAME");
var password = Environment.GetEnvironmentVariable("CAKEMAIL_PASSWORD");
var overrideClientId = Environment.GetEnvironmentVariable("CAKEMAIL_OVERRIDECLIENTID");
+ var proxy = useFiddler ? new WebProxy("http://localhost:8888") : null;
+ var client = new CakeMailRestClient(apiKey, proxy);
+ var loginInfo = client.Users.LoginAsync(userName, password).Result;
+ var clientId = string.IsNullOrEmpty(overrideClientId) ? loginInfo.ClientId : long.Parse(overrideClientId);
+ var userKey = loginInfo.UserKey;
- if (debugHttpMessagesToConsole)
- {
- LogProvider.SetCurrentLogProvider(new ColoredConsoleLogProvider());
- }
-
+ // Configure Console
var source = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
@@ -41,55 +79,100 @@ static async Task Main()
source.Cancel();
};
- try
+ // Ensure the Console is tall enough and centered on the screen
+ Console.WindowHeight = Math.Min(60, Console.LargestWindowHeight);
+ ConsoleUtils.CenterConsole();
+
+ // These are the integration tests that we will execute
+ var integrationTests = new Type[]
{
- var client = new CakeMailRestClient(apiKey, proxy);
- var loginInfo = client.Users.LoginAsync(userName, password).Result;
- var clientId = string.IsNullOrEmpty(overrideClientId) ? loginInfo.ClientId : long.Parse(overrideClientId);
- var userKey = loginInfo.UserKey;
+ typeof(CampaignsTests),
+ typeof(ClientsTests),
+ typeof(CountriesTests),
+ typeof(ListsTests),
+ typeof(MailingsTests),
+ typeof(PermissionsTests),
+ typeof(RelaysTests),
+ typeof(SuppressionListsTests),
+ typeof(TemplatesTests),
+ typeof(TimezonesTests),
+ typeof(TriggersTests),
+ typeof(UsersTests),
+ };
- var tasks = new Task[]
+ // Execute the async tests in parallel (with max degree of parallelism)
+ var results = await integrationTests.ForEachAsync(
+ async integrationTestType =>
{
- ExecuteAsync(client, userKey, clientId, source, TimezonesTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, CountriesTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, ClientsTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, UsersTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, PermissionsTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, CampaignsTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, ListsTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, TemplatesTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, SuppressionListsTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, RelaysTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, TriggersTests.ExecuteAllMethods),
- ExecuteAsync(client, userKey, clientId, source, MailingsTests.ExecuteAllMethods)
- };
- await Task.WhenAll(tasks).ConfigureAwait(false);
- return await Task.FromResult(0); // Success.
- }
- catch (OperationCanceledException)
+ var log = new StringWriter();
+
+ try
+ {
+ var integrationTest = (IIntegrationTest)Activator.CreateInstance(integrationTestType);
+ await integrationTest.Execute(client, userKey, clientId, log, source.Token).ConfigureAwait(false);
+ return (TestName: integrationTestType.Name, ResultCode: ResultCodes.Success, Message: string.Empty);
+ }
+ catch (OperationCanceledException)
+ {
+ await log.WriteLineAsync($"-----> TASK CANCELLED").ConfigureAwait(false);
+ return (TestName: integrationTestType.Name, ResultCode: ResultCodes.Cancelled, Message: "Task cancelled");
+ }
+ catch (Exception e)
+ {
+ var exceptionMessage = e.GetBaseException().Message;
+ await log.WriteLineAsync($"-----> AN EXCEPTION OCCURRED: {exceptionMessage}").ConfigureAwait(false);
+ return (TestName: integrationTestType.Name, ResultCode: ResultCodes.Exception, Message: exceptionMessage);
+ }
+ finally
+ {
+ await Console.Out.WriteLineAsync(log.ToString()).ConfigureAwait(false);
+ }
+ }, MAX_CAKEMAIL_API_CONCURRENCY)
+ .ConfigureAwait(false);
+
+ // Display summary
+ var summary = new StringWriter();
+ await summary.WriteLineAsync("\n\n**************************************************").ConfigureAwait(false);
+ await summary.WriteLineAsync("******************** SUMMARY *********************").ConfigureAwait(false);
+ await summary.WriteLineAsync("**************************************************").ConfigureAwait(false);
+
+ var resultsWithMessage = results
+ .Where(r => !string.IsNullOrEmpty(r.Message))
+ .ToArray();
+
+ if (resultsWithMessage.Any())
{
- return 1223; // Cancelled.
+ foreach (var (TestName, ResultCode, Message) in resultsWithMessage)
+ {
+ const int TEST_NAME_MAX_LENGTH = 25;
+ var name = TestName.Length <= TEST_NAME_MAX_LENGTH ? TestName : TestName.Substring(0, TEST_NAME_MAX_LENGTH - 3) + "...";
+ await summary.WriteLineAsync($"{name.PadRight(TEST_NAME_MAX_LENGTH, ' ')} : {Message}").ConfigureAwait(false);
+ }
}
- catch (Exception e)
+ else
{
- source.Cancel();
- var log = new StringWriter();
- await log.WriteLineAsync("\n\n**************************************************").ConfigureAwait(false);
- await log.WriteLineAsync("**************************************************").ConfigureAwait(false);
- await log.WriteLineAsync($"AN EXCEPTION OCCURED: {e.GetBaseException().Message}").ConfigureAwait(false);
- await log.WriteLineAsync("**************************************************").ConfigureAwait(false);
- await log.WriteLineAsync("**************************************************").ConfigureAwait(false);
- await Console.Out.WriteLineAsync(log.ToString()).ConfigureAwait(false);
- return 1; // Exception
+ await summary.WriteLineAsync("All tests completed succesfully").ConfigureAwait(false);
}
- finally
+
+ await summary.WriteLineAsync("**************************************************").ConfigureAwait(false);
+ await Console.Out.WriteLineAsync(summary.ToString()).ConfigureAwait(false);
+
+ // Prompt user to press a key in order to allow reading the log in the console
+ var promptLog = new StringWriter();
+ await promptLog.WriteLineAsync("\n\n**************************************************").ConfigureAwait(false);
+ await promptLog.WriteLineAsync("Press any key to exit").ConfigureAwait(false);
+ Prompt(promptLog.ToString());
+
+ // Return code indicating success/failure
+ var resultCode = (int)ResultCodes.Success;
+ if (results.Any(result => result.ResultCode != ResultCodes.Success))
{
- var log = new StringWriter();
- await log.WriteLineAsync("\n\n**************************************************").ConfigureAwait(false);
- await log.WriteLineAsync("All tests completed").ConfigureAwait(false);
- await log.WriteLineAsync("Press any key to exit").ConfigureAwait(false);
- Prompt(log.ToString());
+ if (results.Any(result => result.ResultCode == ResultCodes.Exception)) resultCode = (int)ResultCodes.Exception;
+ else if (results.Any(result => result.ResultCode == ResultCodes.Cancelled)) resultCode = (int)ResultCodes.Cancelled;
+ else resultCode = (int)results.First(result => result.ResultCode != ResultCodes.Success).ResultCode;
}
+
+ return await Task.FromResult(resultCode);
}
private static char Prompt(string prompt)
@@ -102,31 +185,5 @@ private static char Prompt(string prompt)
var result = Console.ReadKey();
return result.KeyChar;
}
-
- private static async Task ExecuteAsync(ICakeMailRestClient client, string userKey, long clientId, CancellationTokenSource cts, Func asyncTask)
- {
- var log = new StringWriter();
-
- try
- {
- await asyncTask(client, userKey, clientId, log, cts.Token).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- await log.WriteLineAsync($"-----> TASK CANCELLED").ConfigureAwait(false);
- return 1223; // Cancelled.
- }
- catch (Exception e)
- {
- await log.WriteLineAsync($"-----> AN EXCEPTION OCCURED: {e.GetBaseException().Message}").ConfigureAwait(false);
- throw;
- }
- finally
- {
- await Console.Out.WriteLineAsync(log.ToString()).ConfigureAwait(false);
- }
-
- return 0; // Success
- }
}
}
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/CampaignsTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/CampaignsTests.cs
index 69371a0..8879e0f 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/CampaignsTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/CampaignsTests.cs
@@ -6,9 +6,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class CampaignsTests
+ public class CampaignsTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** CAMPAIGNS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/ClientsTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/ClientsTests.cs
index af21a20..846cfd9 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/ClientsTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/ClientsTests.cs
@@ -7,11 +7,11 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class ClientsTests
+ public class ClientsTests : IIntegrationTest
{
private const int UTC_TIMEZONE_ID = 542;
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** CLIENT *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/CountriesTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/CountriesTests.cs
index 21679fb..c742250 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/CountriesTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/CountriesTests.cs
@@ -5,9 +5,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class CountriesTests
+ public class CountriesTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** COUNTRIES *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/ListsTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/ListsTests.cs
index 9cd7bb8..f9938cf 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/ListsTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/ListsTests.cs
@@ -8,9 +8,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class ListsTests
+ public class ListsTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** LISTS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/MailingsTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/MailingsTests.cs
index b3c86a6..bf3505e 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/MailingsTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/MailingsTests.cs
@@ -1,4 +1,4 @@
-using CakeMail.RestClient.Models;
+using CakeMail.RestClient.Models;
using System;
using System.IO;
using System.Linq;
@@ -7,9 +7,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class MailingsTests
+ public class MailingsTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** MAILINGS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/PermissionsTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/PermissionsTests.cs
index 15f3917..54cb364 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/PermissionsTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/PermissionsTests.cs
@@ -6,9 +6,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class PermissionsTests
+ public class PermissionsTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** PERMISSIONS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/RelaysTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/RelaysTests.cs
index 9e9d28a..2912101 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/RelaysTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/RelaysTests.cs
@@ -5,9 +5,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class RelaysTests
+ public class RelaysTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** RELAYS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/SuppressionListsTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/SuppressionListsTests.cs
index 1fc0a3f..182697f 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/SuppressionListsTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/SuppressionListsTests.cs
@@ -5,9 +5,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class SuppressionListsTests
+ public class SuppressionListsTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** SUPPRESSION LISTS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/TemplatesTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/TemplatesTests.cs
index 9f3ad55..328bf94 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/TemplatesTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/TemplatesTests.cs
@@ -6,9 +6,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class TemplatesTests
+ public class TemplatesTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** TEMPLATES *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/TimezonesTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/TimezonesTests.cs
index 5492dae..7ee7a16 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/TimezonesTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/TimezonesTests.cs
@@ -5,9 +5,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class TimezonesTests
+ public class TimezonesTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** TIMEZONES *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/TriggersTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/TriggersTests.cs
index 9375756..bd1ece2 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/TriggersTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/TriggersTests.cs
@@ -6,9 +6,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class TriggersTests
+ public class TriggersTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** TRIGGERS *****").ConfigureAwait(false);
@@ -77,7 +77,7 @@ public static async Task ExecuteAllMethods(ICakeMailRestClient client, string us
await log.WriteLineAsync($"List deleted: {(listDeleted ? "success" : "failed")}").ConfigureAwait(false);
var campaignDeleted = await client.Campaigns.DeleteAsync(userKey, campaignId, clientId, cancellationToken).ConfigureAwait(false);
- await log.WriteLineAsync($"List deleted: {(campaignDeleted ? "success" : "failed")}").ConfigureAwait(false);
+ await log.WriteLineAsync($"Campaign deleted: {(campaignDeleted ? "success" : "failed")}").ConfigureAwait(false);
}
}
}
diff --git a/Source/CakeMail.RestClient.IntegrationTests/Tests/UsersTests.cs b/Source/CakeMail.RestClient.IntegrationTests/Tests/UsersTests.cs
index b2ad981..24da425 100644
--- a/Source/CakeMail.RestClient.IntegrationTests/Tests/UsersTests.cs
+++ b/Source/CakeMail.RestClient.IntegrationTests/Tests/UsersTests.cs
@@ -6,9 +6,9 @@
namespace CakeMail.RestClient.IntegrationTests.Tests
{
- public static class UsersTests
+ public class UsersTests : IIntegrationTest
{
- public static async Task ExecuteAllMethods(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
+ public async Task Execute(ICakeMailRestClient client, string userKey, long clientId, TextWriter log, CancellationToken cancellationToken)
{
await log.WriteLineAsync("\n***** USERS *****").ConfigureAwait(false);
diff --git a/Source/CakeMail.RestClient.UnitTests/CakeMailRestClientTests.cs b/Source/CakeMail.RestClient.UnitTests/CakeMailRestClientTests.cs
index 0bd130b..33e4dba 100644
--- a/Source/CakeMail.RestClient.UnitTests/CakeMailRestClientTests.cs
+++ b/Source/CakeMail.RestClient.UnitTests/CakeMailRestClientTests.cs
@@ -23,13 +23,25 @@ public class CakeMailRestClientTests
public void Version_is_not_empty()
{
// Arrange
- var client = new CakeMailRestClient(API_KEY);
// Act
- var result = client.Version;
+ var version = CakeMailRestClient.Version;
+
+ // Assert
+ version.ShouldNotBeNullOrEmpty();
+ }
+
+ [Fact]
+ public void UserAgent()
+ {
+ // Arrange
+
+ // Act
+ var userAgent = CakeMailRestClient.UserAgent;
// Assert
- result.ShouldNotBeNullOrEmpty();
+ userAgent.Split(new[] { '/' })[0].ShouldBe("CakeMail .NET REST Client");
+ userAgent.Split(new[] { '+' })[1].Trim(new[] { '(', ')' }).ShouldBe("https://github.com/Jericho/CakeMail.RestClient");
}
[Fact]
@@ -39,12 +51,9 @@ public void RestClient_constructor_with_ApiKey()
// Act
var client = new CakeMailRestClient(API_KEY);
- var userAgent = client.UserAgent;
var baseUrl = client.BaseUrl;
// Assert
- userAgent.Split(new[] { '/' })[0].ShouldBe("CakeMail .NET REST Client");
- userAgent.Split(new[] { '+' })[1].Trim(new[] { '(', ')' }).ShouldBe("https://github.com/Jericho/CakeMail.RestClient");
baseUrl.ShouldBe(new Uri("https://api.wbsrvc.com"));
}
diff --git a/Source/CakeMail.RestClient/CakeMailRestClient.cs b/Source/CakeMail.RestClient/CakeMailRestClient.cs
index ff63705..129416a 100644
--- a/Source/CakeMail.RestClient/CakeMailRestClient.cs
+++ b/Source/CakeMail.RestClient/CakeMailRestClient.cs
@@ -1,3 +1,4 @@
+using CakeMail.RestClient.Logging;
using CakeMail.RestClient.Resources;
using CakeMail.RestClient.Utilities;
using Pathoschild.Http.Client;
@@ -29,14 +30,22 @@ public class CakeMailRestClient : ICakeMailRestClient
#region PROPERTIES
///
- /// Gets the API key provided by CakeMail.
+ /// Gets the Version.
///
- public string ApiKey { get; private set; }
+ ///
+ /// The version.
+ ///
+ public static string Version { get; private set; }
///
/// Gets the user agent.
///
- public string UserAgent { get; private set; }
+ public static string UserAgent { get; private set; }
+
+ ///
+ /// Gets the API key provided by CakeMail.
+ ///
+ public string ApiKey { get; private set; }
///
/// Gets the URL where all API requests are sent.
@@ -108,18 +117,22 @@ public class CakeMailRestClient : ICakeMailRestClient
///
public ITriggers Triggers { get; private set; }
- ///
- /// Gets the Version.
- ///
- ///
- /// Gets the version.
- ///
- public string Version { get; private set; }
-
#endregion
#region CONSTRUCTORS AND DESTRUCTORS
+ ///
+ /// Initializes static members of the class.
+ ///
+ static CakeMailRestClient()
+ {
+ Version = typeof(CakeMailRestClient).GetTypeInfo().Assembly.GetName().Version.ToString(3);
+#if DEBUG
+ Version = "DEBUG";
+#endif
+ UserAgent = $"CakeMail .NET REST Client/{Version} (+https://github.com/Jericho/CakeMail.RestClient)";
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -171,19 +184,14 @@ private CakeMailRestClient(string apiKey, HttpClient httpClient, bool disposeCli
ApiKey = apiKey;
BaseUrl = new Uri(CAKEMAIL_BASE_URI);
- Version = typeof(CakeMailRestClient).GetTypeInfo().Assembly.GetName().Version.ToString(3);
-#if DEBUG
- Version = "DEBUG";
-#endif
- UserAgent = $"CakeMail .NET REST Client/{Version} (+https://github.com/Jericho/CakeMail.RestClient)";
_fluentClient = new FluentClient(this.BaseUrl, httpClient)
- .SetUserAgent(this.UserAgent);
+ .SetUserAgent(CakeMailRestClient.UserAgent);
_fluentClient.Filters.Remove();
// Order is important: DiagnosticHandler must be first.
- _fluentClient.Filters.Add(new DiagnosticHandler(_options.LogBehavior));
+ _fluentClient.Filters.Add(new DiagnosticHandler(_options.LogLevelSuccessfulCalls, _options.LogLevelFailedCalls));
_fluentClient.Filters.Add(new CakeMailErrorHandler());
_fluentClient.BaseClient.DefaultRequestHeaders.Add("apikey", this.ApiKey);
@@ -277,7 +285,11 @@ private CakeMailClientOptions GetDefaultOptions()
{
return new CakeMailClientOptions()
{
- LogBehavior = LogBehavior.LogEverything
+ // Setting to 'Debug' to mimic previous behavior. I think this is a sensible default setting.
+ LogLevelSuccessfulCalls = LogLevel.Debug,
+
+ // Setting to 'Debug' to mimic previous behavior. I think 'Error' would make more sense.
+ LogLevelFailedCalls = LogLevel.Debug
};
}
diff --git a/Source/CakeMail.RestClient/ICakeMailRestClient.cs b/Source/CakeMail.RestClient/ICakeMailRestClient.cs
index 4d760a6..331461c 100644
--- a/Source/CakeMail.RestClient/ICakeMailRestClient.cs
+++ b/Source/CakeMail.RestClient/ICakeMailRestClient.cs
@@ -1,4 +1,4 @@
-using CakeMail.RestClient.Resources;
+using CakeMail.RestClient.Resources;
using System;
namespace CakeMail.RestClient
@@ -13,11 +13,6 @@ public interface ICakeMailRestClient
///
string ApiKey { get; }
- ///
- /// Gets the user agent.
- ///
- string UserAgent { get; }
-
///
/// Gets the URL where all API requests are sent.
///
@@ -87,13 +82,5 @@ public interface ICakeMailRestClient
/// Gets the Triggers resource.
///
ITriggers Triggers { get; }
-
- ///
- /// Gets the Version.
- ///
- ///
- /// Gets the version.
- ///
- string Version { get; }
}
}
diff --git a/Source/CakeMail.RestClient/Properties/AssemblyInfo.cs b/Source/CakeMail.RestClient/Properties/AssemblyInfo.cs
index 8d522dc..a4b453c 100644
--- a/Source/CakeMail.RestClient/Properties/AssemblyInfo.cs
+++ b/Source/CakeMail.RestClient/Properties/AssemblyInfo.cs
@@ -1,5 +1,6 @@
-using System;
+using System;
using System.Runtime.CompilerServices;
[assembly: CLSCompliant(true)]
[assembly: InternalsVisibleTo("CakeMail.RestClient.UnitTests")]
+[assembly: InternalsVisibleTo("CakeMail.RestClient.IntegrationTests")]
diff --git a/Source/CakeMail.RestClient/Utilities/CakeMailClientOptions.cs b/Source/CakeMail.RestClient/Utilities/CakeMailClientOptions.cs
index 3627ad2..f09362c 100644
--- a/Source/CakeMail.RestClient/Utilities/CakeMailClientOptions.cs
+++ b/Source/CakeMail.RestClient/Utilities/CakeMailClientOptions.cs
@@ -1,3 +1,5 @@
+using CakeMail.RestClient.Logging;
+
namespace CakeMail.RestClient.Utilities
{
///
@@ -6,8 +8,13 @@ namespace CakeMail.RestClient.Utilities
public class CakeMailClientOptions
{
///
- /// Gets or sets the logging behavior.
+ /// Gets or sets the log levels for successful calls (HTTP status code in the 200-299 range).
///
- public LogBehavior LogBehavior { get; set; }
+ public LogLevel LogLevelSuccessfulCalls { get; set; }
+
+ ///
+ /// Gets or sets the log levels for failed calls (HTTP status code outside of the 200-299 range).
+ ///
+ public LogLevel LogLevelFailedCalls { get; set; }
}
}
diff --git a/Source/CakeMail.RestClient/Utilities/DiagnosticHandler.cs b/Source/CakeMail.RestClient/Utilities/DiagnosticHandler.cs
index 6a67369..3a61349 100644
--- a/Source/CakeMail.RestClient/Utilities/DiagnosticHandler.cs
+++ b/Source/CakeMail.RestClient/Utilities/DiagnosticHandler.cs
@@ -2,7 +2,7 @@
using Pathoschild.Http.Client;
using Pathoschild.Http.Client.Extensibility;
using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
@@ -21,21 +21,23 @@ internal class DiagnosticHandler : IHttpFilter
internal const string DIAGNOSTIC_ID_HEADER_NAME = "CakeMailRestClient-Diagnostic-Id";
private static readonly ILog _logger = LogProvider.For();
- private readonly LogBehavior _logBehavior;
+ private readonly LogLevel _logLevelSuccessfulCalls;
+ private readonly LogLevel _logLevelFailedCalls;
#endregion
#region PROPERTIES
- internal static IDictionary RequestReference, StringBuilder Diagnostic, long RequestTimestamp, long ResponseTimeStamp)> DiagnosticsInfo { get; } = new Dictionary, StringBuilder, long, long)>();
+ internal static ConcurrentDictionary RequestReference, string Diagnostic, long RequestTimestamp, long ResponseTimeStamp)> DiagnosticsInfo { get; } = new ConcurrentDictionary, string, long, long)>();
#endregion
#region CTOR
- public DiagnosticHandler(LogBehavior logBehavior)
+ public DiagnosticHandler(LogLevel logLevelSuccessfulCalls, LogLevel logLevelFailedCalls)
{
- _logBehavior = logBehavior;
+ _logLevelSuccessfulCalls = logLevelSuccessfulCalls;
+ _logLevelFailedCalls = logLevelFailedCalls;
}
#endregion
@@ -60,10 +62,7 @@ public void OnRequest(IRequest request)
LogContent(diagnostic, httpRequest.Content);
// Add the diagnotic info to our cache
- lock (DiagnosticsInfo)
- {
- DiagnosticsInfo.Add(diagnosticId, (new WeakReference(request.Message), diagnostic, Stopwatch.GetTimestamp(), long.MinValue));
- }
+ DiagnosticsInfo.TryAdd(diagnosticId, (new WeakReference(request.Message), diagnostic.ToString(), Stopwatch.GetTimestamp(), long.MinValue));
}
/// Method invoked just after the HTTP response is received. This method can modify the incoming HTTP response.
@@ -74,63 +73,52 @@ public void OnResponse(IResponse response, bool httpErrorAsException)
var responseTimestamp = Stopwatch.GetTimestamp();
var httpResponse = response.Message;
- var diagnosticId = response.Message.RequestMessage.Headers.GetValue(DiagnosticHandler.DIAGNOSTIC_ID_HEADER_NAME);
- var diagnosticInfo = DiagnosticsInfo[diagnosticId];
- diagnosticInfo.ResponseTimeStamp = responseTimestamp;
-
- try
+ var diagnosticId = response.Message.RequestMessage.Headers.GetValue(DIAGNOSTIC_ID_HEADER_NAME);
+ if (DiagnosticsInfo.TryGetValue(diagnosticId, out (WeakReference RequestReference, string Diagnostic, long RequestTimestamp, long ResponseTimestamp) diagnosticInfo))
{
- // Log the response
- diagnosticInfo.Diagnostic.AppendLine();
- diagnosticInfo.Diagnostic.AppendLine("RESPONSE:");
- diagnosticInfo.Diagnostic.AppendLine($" HTTP/{httpResponse.Version} {(int)httpResponse.StatusCode} {httpResponse.ReasonPhrase}");
- LogHeaders(diagnosticInfo.Diagnostic, httpResponse.Headers);
- LogContent(diagnosticInfo.Diagnostic, httpResponse.Content);
-
- // Calculate how much time elapsed between request and response
- var elapsed = TimeSpan.FromTicks(diagnosticInfo.ResponseTimeStamp - diagnosticInfo.RequestTimestamp);
-
- // Log diagnostic
- diagnosticInfo.Diagnostic.AppendLine();
- diagnosticInfo.Diagnostic.AppendLine("DIAGNOSTIC:");
- diagnosticInfo.Diagnostic.AppendLine($" The request took {elapsed.ToDurationString()}");
- }
- catch (Exception e)
- {
- Debug.WriteLine("{0}\r\nAN EXCEPTION OCCURRED: {1}\r\n{0}", new string('=', 50), e.GetBaseException().Message);
- diagnosticInfo.Diagnostic.AppendLine($"AN EXCEPTION OCCURRED: {e.GetBaseException().Message}");
-
- if (_logger != null && _logger.IsErrorEnabled())
+ var updatedDiagnostic = new StringBuilder(diagnosticInfo.Diagnostic);
+ try
{
- _logger.Error(e, "An exception occurred when inspecting the response from CakeMail");
+ // Log the response
+ updatedDiagnostic.AppendLine();
+ updatedDiagnostic.AppendLine("RESPONSE:");
+ updatedDiagnostic.AppendLine($" HTTP/{httpResponse.Version} {(int)httpResponse.StatusCode} {httpResponse.ReasonPhrase}");
+ LogHeaders(updatedDiagnostic, httpResponse.Headers);
+ LogContent(updatedDiagnostic, httpResponse.Content);
+
+ // Calculate how much time elapsed between request and response
+ var elapsed = TimeSpan.FromTicks(responseTimestamp - diagnosticInfo.RequestTimestamp);
+
+ // Log diagnostic
+ updatedDiagnostic.AppendLine();
+ updatedDiagnostic.AppendLine("DIAGNOSTIC:");
+ updatedDiagnostic.AppendLine($" The request took {elapsed.ToDurationString()}");
}
- }
- finally
- {
- var diagnosticMessage = diagnosticInfo.Diagnostic.ToString();
-
- if (!string.IsNullOrEmpty(diagnosticMessage))
+ catch (Exception e)
{
- Debug.WriteLine("{0}\r\n{1}{0}", new string('=', 50), diagnosticMessage);
+ Debug.WriteLine("{0}\r\nAN EXCEPTION OCCURRED: {1}\r\n{0}", new string('=', 50), e.GetBaseException().Message);
+ updatedDiagnostic.AppendLine($"AN EXCEPTION OCCURRED: {e.GetBaseException().Message}");
- if (_logger != null && _logger.IsDebugEnabled())
+ if (_logger != null && _logger.IsErrorEnabled())
{
- var shouldLog = response.IsSuccessStatusCode && _logBehavior.HasFlag(LogBehavior.LogSuccessfulCalls);
- shouldLog |= !response.IsSuccessStatusCode && _logBehavior.HasFlag(LogBehavior.LogFailedCalls);
-
- if (shouldLog)
- {
- _logger.Debug(diagnosticMessage
- .Replace("{", "{{")
- .Replace("}", "}}"));
- }
+ _logger.Error(e, "An exception occurred when inspecting the response from SendGrid");
}
}
+ finally
+ {
+ var diagnosticMessage = updatedDiagnostic.ToString();
- DiagnosticsInfo[diagnosticId] = diagnosticInfo;
+ LogDiagnostic(response.IsSuccessStatusCode, _logLevelSuccessfulCalls, diagnosticMessage);
+ LogDiagnostic(!response.IsSuccessStatusCode, _logLevelFailedCalls, diagnosticMessage);
- Cleanup();
+ DiagnosticsInfo.TryUpdate(
+ diagnosticId,
+ (diagnosticInfo.RequestReference, updatedDiagnostic.ToString(), diagnosticInfo.RequestTimestamp, responseTimestamp),
+ (diagnosticInfo.RequestReference, diagnosticInfo.Diagnostic, diagnosticInfo.RequestTimestamp, diagnosticInfo.ResponseTimestamp));
+ }
}
+
+ Cleanup();
}
#endregion
@@ -179,6 +167,20 @@ private void LogContent(StringBuilder diagnostic, HttpContent httpContent)
}
}
+ private void LogDiagnostic(bool shouldLog, LogLevel logLEvel, string diagnosticMessage)
+ {
+ if (shouldLog && _logger != null)
+ {
+ var logLevelEnabled = _logger.Log(logLEvel, null, null, Array.Empty