Skip to content

Commit

Permalink
Merge pull request #93 from pusher/add-channel-attributes
Browse files Browse the repository at this point in the history
Major: Add channel attributes
  • Loading branch information
MeenaAlfons authored May 1, 2023
2 parents d0c2449 + 5d3e11b commit a2cf9c9
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 5.0.0
* [ADDED] Add support for channel attributes when triggering events

## 4.7.0-beta.2
* [ADDED] Constructor for PusherRestClient accepting an externally supplied HttpClient

Expand Down
94 changes: 94 additions & 0 deletions PusherServer.Tests/AcceptanceTests/Trigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,98 @@ public async Task It_should_expose_a_multiple_event_ids_when_publishing_to_multi
Assert.AreEqual(channels.Length, result.EventIds.Count);
}
}

[TestFixture]
public class When_requesting_channel_attributes
{
static Random random = new Random();

[Test]
public async Task It_should_return_channel_attributes_for_multiple_channels()
{
var pusher = ClientServerFactory.CreateServer();
var channels = new List<string>
{
GetPublicChannelName(),
GetPublicChannelName(),
};
var options = new TriggerOptions
{
Info = new List<string> { "subscription_count" },
};
await ClientServerFactory.CreateClientAsync(pusher, channels[0]).ConfigureAwait(false);
await ClientServerFactory.CreateClientAsync(pusher, channels[1]).ConfigureAwait(false);

var result = await pusher.TriggerAsync(channels.ToArray(), "event", "data", options).ConfigureAwait(false);

Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual(2, result.ChannelAttributes.Count);
Assert.AreEqual(1, result.ChannelAttributes[channels[0]].subscription_count);
Assert.AreEqual(1, result.ChannelAttributes[channels[1]].subscription_count);
}

[Test]
public async Task It_should_return_subscription_count_for_any_channel()
{
var channelName = GetPublicChannelName();
var pusher = ClientServerFactory.CreateServer();
await ClientServerFactory.CreateClientAsync(pusher, channelName).ConfigureAwait(false);

var options = new TriggerOptions
{
Info = new List<string>() { "subscription_count" },
};
var result = await pusher.TriggerAsync(channelName, "event", "data", options).ConfigureAwait(false);

Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual(1, result.ChannelAttributes[channelName].subscription_count);
}

[Test]
public async Task It_should_return_user_count_for_presence_channels()
{
var channelName = GetPresenceChannelName();
var pusher = ClientServerFactory.CreateServer();
await ClientServerFactory.CreateClientAsync(pusher, channelName).ConfigureAwait(false);

var options = new TriggerOptions
{
Info = new List<string>() { "user_count" },
};
var result = await pusher.TriggerAsync(channelName, "event", "data", options).ConfigureAwait(false);

Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual(1, result.ChannelAttributes[channelName].user_count);
}

[Test]
public async Task It_should_return_all_requested_attributes()
{
var channelName = GetPresenceChannelName();
var pusher = ClientServerFactory.CreateServer();
await ClientServerFactory.CreateClientAsync(pusher, channelName).ConfigureAwait(false);
await ClientServerFactory.CreateClientAsync(pusher, channelName).ConfigureAwait(false);
await ClientServerFactory.CreateClientAsync(pusher, channelName).ConfigureAwait(false);

var options = new TriggerOptions
{
Info = new List<string>() { "subscription_count", "user_count" },
};
var result = await pusher.TriggerAsync(channelName, "event", "data", options).ConfigureAwait(false);

Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual(1, result.ChannelAttributes[channelName].user_count);
Assert.AreEqual(3, result.ChannelAttributes[channelName].subscription_count);
}

private static string GetPublicChannelName()
{
return $"test-channel-{random.Next()}";
}

private static string GetPresenceChannelName()
{
return $"presence-test-channel-{random.Next()}";
}
}
}
57 changes: 41 additions & 16 deletions PusherServer.Tests/UnitTests/TriggerResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using NSubstitute;
using NUnit.Framework;
using PusherServer.Exceptions;
using PusherServer.Tests.Helpers;

using PusherServer.Tests.Helpers;

namespace PusherServer.Tests.UnitTests
{
public class TriggerResultHelper
Expand All @@ -18,6 +18,17 @@ public class TriggerResultHelper
"}" +
"}";

public static string TRIGGER_RESPONSE_WITH_CHANNEL_ATTRIBUTES = "{" +
"\"channels\": {" +
"\"any-channel\": {" +
"\"user_count\": 1," +
"\"subscription_count\": 2" +
"}" +
"}," +
"\"event_ids\": {" +
"\"any-channel\": \"test-id\"" +
"}" +
"}";
}

[TestFixture]
Expand Down Expand Up @@ -57,20 +68,20 @@ public void it_should_have_no_event_id_value_when_a_v7_protocol_200_response_is_
[Test]
[ExpectedException(typeof (TriggerResponseException))]
public void it_should_treat_non_JSON_content_in_the_request_body_as_a_failed_request()
{
HttpResponseMessage response = Substitute.For<HttpResponseMessage>();
response.Content = new StringContent("FISH");
response.StatusCode = HttpStatusCode.OK;

new TriggerResult(response, "FISH");
try
{
new TriggerResult(response, "FISH");
Assert.Fail("Exception was not thrown");
}
catch (TriggerResponseException ex)
{
Assert.IsTrue(ex.Message.Contains("FISH"), "exception message includes response");
{
HttpResponseMessage response = Substitute.For<HttpResponseMessage>();
response.Content = new StringContent("FISH");
response.StatusCode = HttpStatusCode.OK;

new TriggerResult(response, "FISH");
try
{
new TriggerResult(response, "FISH");
Assert.Fail("Exception was not thrown");
}
catch (TriggerResponseException ex)
{
Assert.IsTrue(ex.Message.Contains("FISH"), "exception message includes response");
}
}

Expand All @@ -85,5 +96,19 @@ public void it_should_not_be_possible_to_change_EventIds()

triggerResult.EventIds.Add("fish", "pie");
}

[Test]
public void it_should_parse_channel_attributes()
{
HttpResponseMessage response = Substitute.For<HttpResponseMessage>();
response.StatusCode = HttpStatusCode.OK;
response.Content = new StringContent(TriggerResultHelper.TRIGGER_RESPONSE_WITH_CHANNEL_ATTRIBUTES);

var triggerResult = new TriggerResult(response, TriggerResultHelper.TRIGGER_RESPONSE_WITH_CHANNEL_ATTRIBUTES);

Assert.AreEqual(2, triggerResult.ChannelAttributes["any-channel"].subscription_count);
Assert.AreEqual(1, triggerResult.ChannelAttributes["any-channel"].user_count);
Assert.AreEqual(1, triggerResult.EventIds.Count);
}
}
}
18 changes: 18 additions & 0 deletions PusherServer/ChannelAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace PusherServer
{
/// <summary>
/// Channel attributes as returned in the trigger event endpoint
/// </summary>
public class ChannelAttributes
{
/// <summary>
/// Number of distinct users currently subscribed to each channel (a single user may be subscribed many times, but will only count as one)
/// </summary>
public int user_count { get; set; }

/// <summary>
/// Number of connections currently subscribed to each channel. This attribute is not available by default. To enable it, navigate to your Channels dashboard, find the app you are working on, and click App Settings.
/// </summary>
public int subscription_count { get; set; }
}
}
6 changes: 6 additions & 0 deletions PusherServer/EventIdData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace PusherServer
public class EventIdData
{
private readonly Dictionary<string, string> _eventIds = new Dictionary<string, string>();
private readonly Dictionary<string, ChannelAttributes> _channelStates = new Dictionary<string, ChannelAttributes>();

/// <summary>
/// Dictionary of channel name to event ID for the triggered event.
Expand All @@ -20,5 +21,10 @@ public Dictionary<string, string> event_ids
return _eventIds;
}
}

/// <summary>
/// Dictionary of channel name to channel attributes
/// </summary>
public Dictionary<string, ChannelAttributes> channels => _channelStates;
}
}
9 changes: 8 additions & 1 deletion PusherServer/ITriggerOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace PusherServer
using System.Collections.Generic;

namespace PusherServer
{
/// <summary>
/// Additional options that can be used when triggering an event.
Expand All @@ -9,5 +11,10 @@ public interface ITriggerOptions
/// Gets or sets the Socket ID for a consuming Trigger
/// </summary>
string SocketId { get; set; }

/// <summary>
/// List of attributes that should be returned for each unique channel triggered to.
/// </summary>
List<string> Info { get; set; }
}
}
5 changes: 5 additions & 0 deletions PusherServer/ITriggerResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ public interface ITriggerResult: IRequestResult
/// Gets the Event IDs related to this Trigger Event
/// </summary>
IDictionary<string, string> EventIds { get; }

/// <summary>
/// If requested via trigger options, returns channel attributes for each channel in Trigger Event request
/// </summary>
IDictionary<string, ChannelAttributes> ChannelAttributes { get; }
}
}
6 changes: 6 additions & 0 deletions PusherServer/Pusher.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using PusherServer.RestfulClient;
Expand Down Expand Up @@ -161,6 +162,11 @@ private TriggerBody CreateTriggerBody(string[] channelNames, string eventName, o
bodyData.socket_id = options.SocketId;
}

if (options.Info != null && options.Info.Count > 0)
{
bodyData.info = string.Join(",", options.Info);
}

return bodyData;
}

Expand Down
9 changes: 8 additions & 1 deletion PusherServer/TriggerBody.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace PusherServer
using System.Collections.Generic;

namespace PusherServer
{
/// <summary>
/// Represents the payload to be sent when triggering events
Expand All @@ -24,5 +26,10 @@ internal class TriggerBody
/// The id of a socket to be excluded from receiving the event.
/// </summary>
public string socket_id { get; set; }

/// <summary>
/// A comma-separated list of attributes that should be returned for each unique channel triggered to.
/// </summary>
public string info { get; set; }
}
}
7 changes: 7 additions & 0 deletions PusherServer/TriggerOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

using System.Collections.Generic;

namespace PusherServer
{
/// <summary>
Expand All @@ -10,5 +12,10 @@ public class TriggerOptions: ITriggerOptions
/// Gets or sets the Socket ID for the consuming Trigger
/// </summary>
public string SocketId { get; set; }

/// <summary>
/// List of attributes that should be returned for each unique channel triggered to.
/// </summary>
public List<string> Info { get; set; }
}
}
2 changes: 2 additions & 0 deletions PusherServer/TriggerResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ public TriggerResult(HttpResponseMessage response, string responseContent) : bas
}

EventIds = new ReadOnlyDictionary<string, string>(eventIdData.event_ids);
ChannelAttributes = new ReadOnlyDictionary<string, ChannelAttributes>(eventIdData.channels);
}

/// <inheritDoc/>
public IDictionary<string, string> EventIds { get; }
public IDictionary<string, ChannelAttributes> ChannelAttributes { get; }
}
}
6 changes: 3 additions & 3 deletions Root.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
<PackageTags>pusher channels realtime websocket</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>icon-128.png</PackageIcon>
<Version>4.7.0-beta.2</Version>
<AssemblyVersion>4.7.0.0</AssemblyVersion>
<FileVersion>4.7.0.0</FileVersion>
<Version>5.0.0</Version>
<AssemblyVersion>5.0.0.0</AssemblyVersion>
<FileVersion>5.0.0.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit a2cf9c9

Please sign in to comment.