Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into task/use-long-bounc…
Browse files Browse the repository at this point in the history
…e-ids
  • Loading branch information
vladsandu committed Apr 6, 2020
2 parents 7fce56f + 58e6d78 commit 8c96389
Show file tree
Hide file tree
Showing 15 changed files with 453 additions and 1 deletion.
131 changes: 131 additions & 0 deletions src/Postmark.Tests/ClientSuppressionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using Xunit;
using PostmarkDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Postmark.Model.Suppressions;
using PostmarkDotNet.Model;

namespace Postmark.Tests
{
public class ClientSuppressionTests : ClientBaseFixture, IDisposable
{
private PostmarkAdminClient _adminClient;
private PostmarkServer _server;

protected override void Setup()
{
_adminClient = new PostmarkAdminClient(WRITE_ACCOUNT_TOKEN, BASE_URL);
_server = TestUtils.MakeSynchronous(() => _adminClient.CreateServerAsync($"integration-test-suppressions-{Guid.NewGuid()}"));
_client = new PostmarkClient(_server.ApiTokens.First(), BASE_URL);
}

[Fact]
public async Task ClientCanSuppressRecipients()
{
var suppressionRequest = new PostmarkSuppressionChangeRequest { EmailAddress = $"test-{Guid.NewGuid().ToString()}@gmail.com" };
var result = await _client.CreateSuppressions(new List<PostmarkSuppressionChangeRequest> { suppressionRequest });

Assert.Single(result.Suppressions);

var suppressionResult = result.Suppressions.First();
Assert.Equal(suppressionRequest.EmailAddress, suppressionResult.EmailAddress);
Assert.Equal(PostmarkSuppressionRequestStatus.Suppressed, suppressionResult.Status);
Assert.Null(suppressionResult.Message);
}

[Fact]
public async Task InvalidRequest_HasFailedStatus()
{
var suppressionRequest = new PostmarkSuppressionChangeRequest { EmailAddress = "not-a-correct-email-address" };
var result = await _client.CreateSuppressions(new List<PostmarkSuppressionChangeRequest> { suppressionRequest });

Assert.Single(result.Suppressions);

var suppressionResult = result.Suppressions.First();
Assert.Equal(suppressionRequest.EmailAddress, suppressionResult.EmailAddress);
Assert.Equal(PostmarkSuppressionRequestStatus.Failed, suppressionResult.Status);
Assert.NotNull(suppressionResult.Message);
}

[Fact]
public async Task ClientCanDeleteSuppressions()
{
var reactivationRequest = new PostmarkSuppressionChangeRequest { EmailAddress = $"test-{Guid.NewGuid().ToString()}@gmail.com" };
var result = await _client.DeleteSuppressions(new List<PostmarkSuppressionChangeRequest> { reactivationRequest });

Assert.Single(result.Suppressions);

var reactivationResult = result.Suppressions.First();
Assert.Equal(reactivationRequest.EmailAddress, reactivationResult.EmailAddress);
Assert.Equal(PostmarkReactivationRequestStatus.Deleted, reactivationResult.Status);
Assert.Null(reactivationResult.Message);
}

[Fact]
public async Task ClientCanListSuppressions()
{
var suppressionRequests = new List<PostmarkSuppressionChangeRequest>();

for (var i = 0; i < 5; i++)
{
var suppressionRequest = new PostmarkSuppressionChangeRequest { EmailAddress = $"test-{Guid.NewGuid().ToString()}@gmail.com" };
suppressionRequests.Add(suppressionRequest);
}

var suppressionResult = await _client.CreateSuppressions(suppressionRequests);
Assert.Equal(5, suppressionResult.Suppressions.Count());
Assert.True(suppressionResult.Suppressions.All(k => k.Status == PostmarkSuppressionRequestStatus.Suppressed));

// Suppressions are being processed asynchronously so we must give it some time to process those requests
var suppressionListing = await TestUtils.PollUntil(() => _client.ListSuppressions(new PostmarkSuppressionQuery()),
k => k.Suppressions.Count() == 5);

Assert.Equal(5, suppressionListing.Suppressions.Count());
}

[Fact]
public async Task ClientCanFilterSuppressionsByEmail()
{
var suppressionRequests = new List<PostmarkSuppressionChangeRequest>();

for (var i = 0; i < 3; i++)
{
var suppressionRequest = new PostmarkSuppressionChangeRequest { EmailAddress = $"test-{Guid.NewGuid().ToString()}@gmail.com" };
suppressionRequests.Add(suppressionRequest);
}

await _client.CreateSuppressions(suppressionRequests);

var query = new PostmarkSuppressionQuery
{
EmailAddress = suppressionRequests.First().EmailAddress
};

// Suppressions are being processed asynchronously so we must give it some time to process those requests
var suppressionListing = await TestUtils.PollUntil(() => _client.ListSuppressions(query), k => k.Suppressions.Count() == 1);

Assert.Single(suppressionListing.Suppressions);

var actualSuppression = suppressionListing.Suppressions.First();

Assert.Equal(suppressionRequests.First().EmailAddress, actualSuppression.EmailAddress);
Assert.Equal("Customer", actualSuppression.Origin);
Assert.Equal("ManualSuppression", actualSuppression.SuppressionReason);
}

private Task Cleanup()
{
return Task.Run(async () =>
{
await _adminClient.DeleteServerAsync(_server.ID);
});
}

public void Dispose()
{
Cleanup().Wait();
}
}
}
2 changes: 1 addition & 1 deletion src/Postmark.Tests/ClientWebhookTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ClientWebhookTests : ClientBaseFixture, IDisposable
protected override void Setup()
{
_adminClient = new PostmarkAdminClient(WRITE_ACCOUNT_TOKEN, BASE_URL);
_server = _adminClient.CreateServerAsync($"integration-test-webhooks-{Guid.NewGuid()}").Result;
_server = TestUtils.MakeSynchronous(() => _adminClient.CreateServerAsync($"integration-test-webhooks-{Guid.NewGuid()}"));
_client = new PostmarkClient(_server.ApiTokens.First(), BASE_URL);
}

Expand Down
42 changes: 42 additions & 0 deletions src/Postmark.Tests/TestUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Threading.Tasks;

namespace Postmark.Tests
{
/// <summary>
/// Utility methods useful in integration and unit tests.
/// </summary>
public static class TestUtils
{
public static T MakeSynchronous<T>(Func<Task<T>> t)
{
var f = Task.Run(async () => await t().ConfigureAwait(false));
f.Wait();
return f.Result;
}

/// <summary>
/// Run a task until the provided condition is met or the number of retries reaches 0.
/// </summary>
/// <typeparam name="T">Type of the returned result.</typeparam>
/// <param name="pollingTaskFunc">Function that generates the task to be polled.</param>
/// <param name="isComplete">Condition that stops the polling if true.</param>
/// <param name="retriesLeft">Number of retries left. Defaults to 5.</param>
/// <param name="delayInMs">Number of milliseconds to add delay before the next poll attempt. Defaults to 1500ms.</param>
/// <returns></returns>
public static async Task<T> PollUntil<T>(Func<Task<T>> pollingTaskFunc, Func<T, bool> isComplete, int retriesLeft = 5,
int delayInMs = 1500)
{
var result = await pollingTaskFunc();

if (isComplete(result) || retriesLeft == 0)
{
return result;
}

await Task.Delay(delayInMs).ConfigureAwait(false);

return await PollUntil(pollingTaskFunc, isComplete, --retriesLeft);
}
}
}
12 changes: 12 additions & 0 deletions src/Postmark/Model/Suppressions/PostmarkBulkReactivationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Result of applying Reactivation requests in bulk.
/// </summary>
public class PostmarkBulkReactivationResult
{
public IEnumerable<PostmarkReactivationRequestResult> Suppressions { get; set; }
}
}
12 changes: 12 additions & 0 deletions src/Postmark/Model/Suppressions/PostmarkBulkSuppressionResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Result of applying Suppression requests in bulk.
/// </summary>
public class PostmarkBulkSuppressionResult
{
public IEnumerable<PostmarkSuppressionRequestResult> Suppressions { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Result of applying a reactivation request.
/// </summary>
public class PostmarkReactivationRequestResult
{
/// <summary>
/// Recipient email address requested to be reactivated.
/// </summary>
public string EmailAddress { get; set; }

/// <summary>
/// Status of the request.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public PostmarkReactivationRequestStatus Status { get; set; }

/// <summary>
/// Optional message regarding the status of this request.
/// </summary>
public string Message { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Postmark.Model.Suppressions
{
/// <summary>
/// Status for the result of reactivating a recipient.
/// </summary>
public enum PostmarkReactivationRequestStatus
{
/// <summary>
/// We have received the request to reactivate the recipient.
/// </summary>
Deleted,

/// <summary>
/// Request submission has failed. Please see the result Message field for information.
/// </summary>
Failed
}
}
32 changes: 32 additions & 0 deletions src/Postmark/Model/Suppressions/PostmarkSuppression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Model representing a Suppressed recipient.
/// For more information about the Suppressions API and possible options, please visit:
/// https://postmarkapp.com/developer/api/suppressions-api
/// </summary>
public class PostmarkSuppression
{
/// <summary>
/// Email address of the suppressed recipient.
/// </summary>
public string EmailAddress { get; set; }

/// <summary>
/// Reason why this recipient was suppressed. E.g.: ManualSuppression, HardBounce, SpamComplaint.
/// </summary>
public string SuppressionReason { get; set; }

/// <summary>
/// Origin that suppressed this recipient. E.g.: Customer, Recipient, Admin.
/// </summary>
public string Origin { get; set; }

/// <summary>
/// Date when the suppression was created.
/// </summary>
public DateTime CreatedAt { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Postmark.Model.Suppressions
{
/// <summary>
/// A request to suppress or reactivate one recipient.
/// </summary>
public class PostmarkSuppressionChangeRequest
{
/// <summary>
/// Address of the recipient whose suppression status should be changed.
/// </summary>
public string EmailAddress { get; set; }
}
}
15 changes: 15 additions & 0 deletions src/Postmark/Model/Suppressions/PostmarkSuppressionListing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Response model for listing Suppressions.
/// </summary>
public class PostmarkSuppressionListing
{
/// <summary>
/// Active Suppressions
/// </summary>
public IEnumerable<PostmarkSuppression> Suppressions { get; set; }
}
}
39 changes: 39 additions & 0 deletions src/Postmark/Model/Suppressions/PostmarkSuppressionQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Query parameters for listing Suppressions.
/// </summary>
public class PostmarkSuppressionQuery
{
/// <summary>
/// Filter Suppressions by reason. (optional)
/// E.g.: ManualSuppression, HardBounce, SpamComplaint.
/// </summary>
/// <remarks>If not provided, Suppressions for all reasons will be returned.</remarks>
public string SuppressionReason { get; set; }

/// <summary>
/// Filter Suppressions by the origin that created them. (optional)
/// E.g.: Customer, Recipient, Admin.
/// </summary>
/// <remarks>If not provided, Suppressions for all origins will be returned.</remarks>
public string Origin { get; set; }

/// <summary>
/// Filter suppressions up to the date specified - inclusive. (optional)
/// </summary>
public DateTime? ToDate { get; set; }

/// <summary>
/// Filter suppressions from the date specified - inclusive. (optional)
/// </summary>
public DateTime? FromDate { get; set; }

/// <summary>
/// Filter by a specific email address. (optional)
/// </summary>
public string EmailAddress { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Postmark.Model.Suppressions
{
/// <summary>
/// Result of applying a Suppression request.
/// </summary>
public class PostmarkSuppressionRequestResult
{
/// <summary>
/// Recipient email address requested to be suppressed.
/// </summary>
public string EmailAddress { get; set; }

/// <summary>
/// Status of the request.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public PostmarkSuppressionRequestStatus Status { get; set; }

/// <summary>
/// Optional message regarding the status of this request.
/// </summary>
public string Message { get; set; }
}
}
Loading

0 comments on commit 8c96389

Please sign in to comment.