Skip to content

Commit

Permalink
Merge pull request #83 from wildbit/task/add-message-streams-api
Browse files Browse the repository at this point in the history
Add support for MessageStreams API
  • Loading branch information
vladsandu committed May 28, 2020
2 parents 5b3a67e + 06e66a2 commit 7330657
Show file tree
Hide file tree
Showing 7 changed files with 381 additions and 0 deletions.
160 changes: 160 additions & 0 deletions src/Postmark.Tests/ClientMessageStreamTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using Xunit;
using PostmarkDotNet;
using System;
using System.Linq;
using System.Threading.Tasks;
using Postmark.Model.MessageStreams;
using PostmarkDotNet.Model;

namespace Postmark.Tests
{
public class ClientMessageStreamTests : 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-message-stream-{Guid.NewGuid()}"));
_client = new PostmarkClient(_server.ApiTokens.First(), BASE_URL);
}

[Fact]
public async Task ClientCanCreateMessageStream()
{
var id = "test-id";
var streamType = MessageStreamType.Broadcasts;
var streamName = "Test Stream";
var description = "This is a description.";

var messageStream = await _client.CreateMessageStream(id, streamType, streamName, description);

Assert.Equal(id, messageStream.ID);
Assert.Equal(_server.ID, messageStream.ServerID);
Assert.Equal(streamType, messageStream.MessageStreamType);
Assert.Equal(streamName, messageStream.Name);
Assert.Equal(description, messageStream.Description);
Assert.Null(messageStream.UpdatedAt);
Assert.Null(messageStream.ArchivedAt);
}

[Fact]
public async Task ClientCanEditMessageStream()
{
var messageStream = await CreateDummyMessageStream(MessageStreamType.Broadcasts);

var newName = "Updated Stream Name";
var newDescription = "Updated Stream Description";
var updatedMessageStream = await _client.EditMessageStream(messageStream.ID, newName, newDescription);

Assert.Equal(newName, updatedMessageStream.Name);
Assert.Equal(newDescription, updatedMessageStream.Description);
Assert.NotNull(updatedMessageStream.UpdatedAt);
}

[Fact]
public async Task ClientCanGetMessageStream()
{
var expectedMessageStream = await CreateDummyMessageStream(MessageStreamType.Broadcasts);
var actualMessageStream = await _client.GetMessageStream(expectedMessageStream.ID);

Assert.Equal(expectedMessageStream.ID, actualMessageStream.ID);
Assert.Equal(expectedMessageStream.ServerID, actualMessageStream.ServerID);
Assert.Equal(expectedMessageStream.MessageStreamType, actualMessageStream.MessageStreamType);
Assert.Equal(expectedMessageStream.Name, actualMessageStream.Name);
Assert.Equal(expectedMessageStream.Description, actualMessageStream.Description);
}

[Fact]
public async Task ClientCanListMessageStreams()
{
var transactionalStream = await CreateDummyMessageStream(MessageStreamType.Transactional);
var broadcastsStream = await CreateDummyMessageStream(MessageStreamType.Broadcasts);

// Listing All stream types
var listing = await _client.ListMessageStreams(MessageStreamTypeFilter.All);
Assert.Equal(4, listing.TotalCount); // includes the default streams
Assert.Equal(4, listing.MessageStreams.Count());
Assert.Contains(transactionalStream.ID, listing.MessageStreams.Select(k => k.ID));
Assert.Contains(broadcastsStream.ID, listing.MessageStreams.Select(k => k.ID));

// Filtering by stream type
var filteredListing = await _client.ListMessageStreams(MessageStreamTypeFilter.Transactional);
Assert.Equal(2, filteredListing.TotalCount); // includes default stream
Assert.Equal(2, filteredListing.MessageStreams.Count());
Assert.Contains(transactionalStream.ID, listing.MessageStreams.Select(k => k.ID));
}

[Fact]
public async Task ClientCanListArchivedStreams()
{
var transactionalStream = await CreateDummyMessageStream(MessageStreamType.Broadcasts);

await _client.ArchiveMessageStream(transactionalStream.ID);

// By default we are not including archived streams
var filteredListing = await _client.ListMessageStreams(MessageStreamTypeFilter.Broadcasts, includeArchivedStreams: false);
Assert.Equal(0, filteredListing.TotalCount);
Assert.Empty(filteredListing.MessageStreams);

// Including archived streams
var completeListing = await _client.ListMessageStreams(MessageStreamTypeFilter.Broadcasts, includeArchivedStreams: true);
Assert.Equal(1, completeListing.TotalCount);
Assert.Single(completeListing.MessageStreams);
Assert.Equal(transactionalStream.ID, completeListing.MessageStreams.First().ID);
Assert.NotNull(completeListing.MessageStreams.First().ArchivedAt);
}

[Fact]
public async Task ClientCanArchiveStreams()
{
var transactionalStream = await CreateDummyMessageStream(MessageStreamType.Transactional);

var confirmation = await _client.ArchiveMessageStream(transactionalStream.ID);

Assert.Equal(transactionalStream.ID, confirmation.ID);
Assert.Equal(transactionalStream.ServerID, confirmation.ServerID);
Assert.True(confirmation.ExpectedPurgeDate > DateTime.UtcNow);

var fetchedMessageStream = await _client.GetMessageStream(transactionalStream.ID);
Assert.NotNull(fetchedMessageStream.ArchivedAt);
}

[Fact]
public async Task ClientCanUnArchiveStreams()
{
var transactionalStream = await CreateDummyMessageStream(MessageStreamType.Transactional);

await _client.ArchiveMessageStream(transactionalStream.ID);

var unarchivedStream = await _client.UnArchiveMessageStream(transactionalStream.ID);

Assert.Equal(transactionalStream.ID, unarchivedStream.ID);
Assert.Equal(transactionalStream.ServerID, unarchivedStream.ServerID);
Assert.Null(unarchivedStream.ArchivedAt);
}

private async Task<PostmarkMessageStream> CreateDummyMessageStream(MessageStreamType streamType)
{
var id = $"test-{Guid.NewGuid().ToString().Substring(0, 25)}"; // IDs are only 30 characters long.
var streamName = "Dummy Test Stream";
var description = "This is a dummy description.";

return await _client.CreateMessageStream(id, streamType, streamName, description);
}

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

public void Dispose()
{
Cleanup().Wait();
}
}
}
12 changes: 12 additions & 0 deletions src/Postmark/Model/MessageStreams/MessageStreamType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Postmark.Model.MessageStreams
{
/// <summary>
/// Valid types for a message stream.
/// </summary>
public enum MessageStreamType
{
Transactional = 0,
Inbound = 1,
Broadcasts = 2
}
}
10 changes: 10 additions & 0 deletions src/Postmark/Model/MessageStreams/MessageStreamTypeFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Postmark.Model.MessageStreams
{
/// <summary>
/// Valid values for filtering message streams by type.
/// </summary>
public enum MessageStreamTypeFilter
{
Transactional, Inbound, Broadcasts, All
}
}
57 changes: 57 additions & 0 deletions src/Postmark/Model/MessageStreams/PostmarkMessageStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Postmark.Model.MessageStreams
{
/// <summary>
/// Model representing a message stream.
/// For more information about the MessageStreams API, please visit our API documentation.
/// </summary>
public class PostmarkMessageStream
{
/// <summary>
/// User defined identifier for this message stream that is unique at the server level.
/// </summary>
public string ID { get; set; }

/// <summary>
/// Id of the server this stream belongs to.
/// </summary>
public int ServerID { get; set; }

/// <summary>
/// Friendly name of the message stream.
/// </summary>
public string Name { get; set; }

/// <summary>
/// Friendly description of the message stream.
/// </summary>
public string Description { get; set; }

/// <summary>
/// The type of this message Stream. Can be Transactional, Inbound or Broadcasts.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public MessageStreamType MessageStreamType { get; set; }

/// <summary>
/// The date when the message stream was created.
/// </summary>
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime CreatedAt { get; set; }

/// <summary>
/// The date when the message stream was last updated. If null, this message stream was never updated.
/// </summary>
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime? UpdatedAt { get; set; }

/// <summary>
/// The date when this message stream has been archived. If null, this message stream is not in an archival state.
/// </summary>
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime? ArchivedAt { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

namespace Postmark.Model.MessageStreams
{
/// <summary>
/// Confirmation of archiving a message stream.
/// </summary>
public class PostmarkMessageStreamArchivalConfirmation
{
/// <summary>
/// Identifier of the message stream that was archived.
/// </summary>
public string ID { get; set; }

/// <summary>
/// Id of the server where this stream was archived.
/// </summary>
public int ServerID { get; set; }

/// <summary>
/// Expected date when this archived message stream will be removed, alongside associated content.
/// The stream can be unarchived up until this date.
/// </summary>
public DateTime ExpectedPurgeDate { get; set; }
}
}
17 changes: 17 additions & 0 deletions src/Postmark/Model/MessageStreams/PostmarkMessageStreamListing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;

namespace Postmark.Model.MessageStreams
{
/// <summary>
/// List of message streams
/// </summary>
public class PostmarkMessageStreamListing
{
public IEnumerable<PostmarkMessageStream> MessageStreams { get; set; }

/// <summary>
/// Count of total message streams
/// </summary>
public int TotalCount { get; set; }
}
}
99 changes: 99 additions & 0 deletions src/Postmark/PostmarkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Postmark.Model.MessageStreams;
using Postmark.Model.Suppressions;
using PostmarkDotNet.Model.Webhooks;

Expand Down Expand Up @@ -1101,5 +1102,103 @@ public async Task<PostmarkBulkReactivationResult> DeleteSuppressions(IEnumerable
}

#endregion

#region MessageStreams

/// <summary>
/// Create a new message stream on your server.
/// </summary>
/// <param name="id">Identifier for your message stream, unique at server level.</param>
/// <param name="type">Type of the message stream. E.g.: Transactional or Broadcasts.</param>
/// <param name="name">Friendly name for your message stream.</param>
/// <param name="description">Friendly description for your message stream. (optional)</param>
/// <remarks>Currently, you cannot create multiple inbound streams.</remarks>
public async Task<PostmarkMessageStream> CreateMessageStream(string id, MessageStreamType type, string name, string description = null)
{
var body = new Dictionary<string, object>
{
["ID"] = id,
["Name"] = name,
["Description"] = description,
["MessageStreamType"] = type.ToString()
};

var apiUrl = "/message-streams/";

return await ProcessRequestAsync<Dictionary<string, object>, PostmarkMessageStream>(apiUrl, HttpMethod.Post, body);
}

/// <summary>
/// Edit the properties of a message stream.
/// </summary>
/// <param name="id">The identifier for the stream you are trying to update.</param>
/// <param name="name">New friendly name to use. (optional)</param>
/// <param name="description">New description to use. (optional)</param>
public async Task<PostmarkMessageStream> EditMessageStream(string id, string name = null, string description = null)
{
var body = new Dictionary<string, object>
{
["Name"] = name,
["Description"] = description
};

var apiUrl = $"/message-streams/{id}";

return await ProcessRequestAsync<Dictionary<string, object>, PostmarkMessageStream>(apiUrl, new HttpMethod("PATCH"), body);
}

/// <summary>
/// Retrieve details about a message stream.
/// </summary>
/// <param name="id">Identifier of the stream to retrieve details for.</param>
public async Task<PostmarkMessageStream> GetMessageStream(string id)
{
return await ProcessNoBodyRequestAsync<PostmarkMessageStream>($"/message-streams/{id}");
}

/// <summary>
/// Retrieve all message streams on the server.
/// </summary>
/// <param name="messageStreamType">Filter by stream type. E.g.: Transactional. Defaults to: All.</param>
/// <param name="includeArchivedStreams">Include archived streams in the result. Defaults to: false.</param>
public async Task<PostmarkMessageStreamListing> ListMessageStreams(MessageStreamTypeFilter messageStreamType = MessageStreamTypeFilter.All,
bool includeArchivedStreams = false)
{
var parameters = new Dictionary<string, object>
{
["MessageStreamType"] = messageStreamType.ToString(),
["IncludeArchivedStreams"] = includeArchivedStreams
};

return await ProcessNoBodyRequestAsync<PostmarkMessageStreamListing>("/message-streams/", parameters);
}

/// <summary>
/// Archive a message stream. This will disable sending/receiving messages via that stream.
/// The stream will also stop being shown in the Postmark UI.
/// Once a stream has been archived, it will be deleted (alongside associated data) at the ExpectedPurgeDate in the response.
/// </summary>
/// <param name="id">Identifier of the stream to archive.</param>
public async Task<PostmarkMessageStreamArchivalConfirmation> ArchiveMessageStream(string id)
{
var apiUrl = $"/message-streams/{id}/archive";

return await ProcessNoBodyRequestAsync<PostmarkMessageStreamArchivalConfirmation>(apiUrl, verb: HttpMethod.Post);
}

/// <summary>
/// UnArchive a message stream. This will resume sending/receiving via that stream.
/// The stream will also re-appear in the Postmark UI.
/// A stream can be unarchived only before the stream ExpectedPurgeDate.
/// </summary>
/// <param name="id">Identifier of the stream to unArchive.</param>
public async Task<PostmarkMessageStream> UnArchiveMessageStream(string id)
{
var apiUrl = $"/message-streams/{id}/unarchive";

return await ProcessNoBodyRequestAsync<PostmarkMessageStream>(apiUrl, verb: HttpMethod.Post);
}

#endregion
}
}

0 comments on commit 7330657

Please sign in to comment.