diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 311408a..914708b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,262 @@ -_ReSharper.*/ -_UpgradeReport_Files -UpgradeLog.XML -Backup -Download -[Bb]in -[Oo]bj -*.suo -*.user -PusherRESTDotNet/PusherRESTDotNet.1.0.nupkg -packages/ -PusherRESTDotNet.sln.docstates -build -*.testsettings -*.vsmdi -msbuild.log -*.nupkg \ No newline at end of file +_ReSharper.*/ +_UpgradeReport_Files +UpgradeLog.XML +Backup +Download +[Bb]in +[Oo]bj +*.suo +*.user +PusherRESTDotNet/PusherRESTDotNet.1.0.nupkg +packages/ +PusherRESTDotNet.sln.docstates +build +*.testsettings +*.vsmdi +msbuild.log +*.nupkg +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ \ No newline at end of file diff --git a/PusherServer.Tests/AcceptanceTests/Authenticate.cs b/PusherServer.Tests/AcceptanceTests/Authenticate.cs deleted file mode 100644 index d38f155..0000000 --- a/PusherServer.Tests/AcceptanceTests/Authenticate.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using NUnit.Framework; -using PusherServer.Tests.Helpers; - -namespace PusherServer.Tests.AcceptanceTests -{ - [TestFixture] - public class When_authenticating_a_private_subscription - { - [TestFixtureSetUp] - public void Setup() - { - PusherClient.Pusher.Trace.Listeners.Add(new ConsoleTraceListener(true)); - } - - [Test] - public void the_authentication_token_for_a_private_channel_should_be_accepted_by_Pusher() - { - PusherServer.Pusher pusherServer = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - PusherClient.Pusher pusherClient = - new PusherClient.Pusher(Config.AppKey, new PusherClient.PusherOptions() - { - Authorizer = new InMemoryAuthorizer(pusherServer) - }); - pusherClient.Host = Config.WebSocketHost; - - string channelName = "private-channel"; - - bool subscribed = false; - AutoResetEvent reset = new AutoResetEvent(false); - - pusherClient.Connected += new PusherClient.ConnectedEventHandler(delegate(object sender) - { - Debug.WriteLine("connected"); - reset.Set(); - }); - - Debug.WriteLine("connecting"); - pusherClient.Connect(); - - Debug.WriteLine("waiting to connect"); - reset.WaitOne(TimeSpan.FromSeconds(5)); - - Debug.WriteLine("subscribing"); - var channel = pusherClient.Subscribe(channelName); - channel.Subscribed += new PusherClient.SubscriptionEventHandler(delegate(object s) - { - Debug.WriteLine("subscribed"); - subscribed = true; - reset.Set(); - }); - - Debug.WriteLine("waiting to subscribe"); - reset.WaitOne(TimeSpan.FromSeconds(5)); - - Assert.IsTrue(subscribed); - } - - [Test] - public void the_authentication_token_for_a_presence_channel_should_be_accepted_by_Pusher() - { - PusherServer.Pusher pusherServer = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - PusherClient.Pusher pusherClient = - new PusherClient.Pusher(Config.AppKey, new PusherClient.PusherOptions() - { - Authorizer = new InMemoryAuthorizer( - pusherServer, - new PresenceChannelData() - { - user_id = "leggetter", - user_info = new { twitter_id = "@leggetter" } - }) - }); - pusherClient.Host = Config.WebSocketHost; - - string channelName = "presence-channel"; - - bool subscribed = false; - AutoResetEvent reset = new AutoResetEvent(false); - - pusherClient.Connected += new PusherClient.ConnectedEventHandler(delegate(object sender) - { - Debug.WriteLine("connected"); - reset.Set(); - }); - - Debug.WriteLine("connecting"); - pusherClient.Connect(); - - Debug.WriteLine("waiting to connect"); - reset.WaitOne(TimeSpan.FromSeconds(5)); - - Debug.WriteLine("subscribing"); - var channel = pusherClient.Subscribe(channelName); - channel.Subscribed += new PusherClient.SubscriptionEventHandler(delegate(object s) - { - Debug.WriteLine("subscribed"); - subscribed = true; - reset.Set(); - }); - - Debug.WriteLine("waiting to subscribe"); - reset.WaitOne(TimeSpan.FromSeconds(5)); - - Assert.IsTrue(subscribed); - } - } -} diff --git a/PusherServer.Tests/AcceptanceTests/ChannelState.cs b/PusherServer.Tests/AcceptanceTests/ChannelState.cs deleted file mode 100644 index 4330c84..0000000 --- a/PusherServer.Tests/AcceptanceTests/ChannelState.cs +++ /dev/null @@ -1,341 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using NUnit.Framework; -using PusherServer.Tests.Helpers; - -namespace PusherServer.Tests.AcceptanceTests -{ - [TestFixture] - public class When_querying_a_Channel - { - [Test] - public void It_should_return_the_state_When_given_a_channel_name_that_exists() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-state-channel1"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new {info = "user_count"}; - - var result = pusherServer.FetchStateForChannel(channelName, info); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(1, result.Data.User_Count); - } - - [Test] - public void It_should_not_return_the_state_based_When_given_a_channel_name_that_exists_an_bad_attributes() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-state-channel2"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new {info = "does-not-exist"}; - - var result = pusherServer.FetchStateForChannel(channelName, info); - - Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode); - StringAssert.IsMatch("info should be a comma separated list of attributes", - (result as GetResult).OriginalContent); - } - - [Test] - public void It_should_return_the_state_asynchronously_When_given_a_channel_name_that_exists() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-state-channel-async-1"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new { info = "user_count" }; - - IGetResult result = null; - - pusherServer.FetchStateForChannelAsync(channelName, info, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(1, result.Data.User_Count); - } - - [Test] - public void It_should_return_the_state_asynchronously_When_given_a_channel_name_that_exists_and_no_info_object_is_provided() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-state-channel-async-1"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - IGetResult result = null; - - pusherServer.FetchStateForChannelAsync(channelName, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.IsTrue(result.Data.Occupied); - } - - [Test] - public void It_should_not_return_the_state_based_asynchronously_When_given_a_channel_name_that_exists_an_bad_attributes() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-state-channel-async-2"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new { info = "does-not-exist" }; - - IGetResult result = null; - - pusherServer.FetchStateForChannelAsync(channelName, info, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode); - StringAssert.IsMatch("info should be a comma separated list of attributes", (result as GetResult).OriginalContent); - } - - [Test] - public void It_should_throw_an_exception_when_given_an_empty_string_as_a_channel_name() - { - var pusherServer = ClientServerFactory.CreateServer(); - - var info = new { info = "user_count" }; - - ArgumentException caughtException = null; - - try - { - pusherServer.FetchStateForChannel(string.Empty, info); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - [Test] - public void It_should_throw_an_exception_when_given_a_null_as_a_channel_name() - { - var pusherServer = ClientServerFactory.CreateServer(); - - var info = new { info = "user_count" }; - - ArgumentException caughtException = null; - - try - { - pusherServer.FetchStateForChannel(null, info); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - [Test] - public void It_should_throw_an_exception_when_given_an_empty_string_as_a_channel_name_async() - { - var pusherServer = ClientServerFactory.CreateServer(); - - var info = new { info = "user_count" }; - - ArgumentException caughtException = null; - - IGetResult result = null; - - try - { - pusherServer.FetchStateForChannelAsync(string.Empty, info, getResult => - { - result = getResult; - }); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - [Test] - public void It_should_throw_an_exception_when_given_a_null_as_a_channel_name_async() - { - var pusherServer = ClientServerFactory.CreateServer(); - - var info = new { info = "user_count" }; - - ArgumentException caughtException = null; - - IGetResult result = null; - - try - { - pusherServer.FetchStateForChannelAsync(string.Empty, info, getResult => - { - result = getResult; - }); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - private class ChannelStateMessage - { - public bool Occupied { get; set; } - public int User_Count { get; set; } - } - } - - [TestFixture] - public class When_querying_Multiple_Channels - { - [Test] - public void It_should_return_the_state_When_given_a_channel_name_that_exists() - { - AutoResetEvent reset = new AutoResetEvent(false); - - string channelName = "presence-multiple-state-channel3"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new {info = "user_count", filter_by_prefix = "presence-"}; - - var result = pusherServer.FetchStateForChannels(info); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(1, ((((Dictionary)result.Data)["channels"] as Dictionary)["presence-multiple-state-channel3"] as Dictionary)["user_count"]); - } - - [Test] - public void It_should_not_return_the_state_based_When_given_a_channel_name_that_exists_an_bad_attributes() - { - AutoResetEvent reset = new AutoResetEvent(false); - - string channelName = "presence-multiple-state-channel4"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new {info = "does-not-exist"}; - - var result = pusherServer.FetchStateForChannels(info); - - Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode); - StringAssert.IsMatch("info should be a comma separated list of attributes", (result as GetResult).OriginalContent); - } - - [Test] - public void It_should_return_the_state_asynchronously_When_given_a_channel_name_that_exists() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-multiple-state-channel-async-3"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new { info = "user_count", filter_by_prefix = "presence-" }; - - IGetResult result = null; - - pusherServer.FetchStateForChannelsAsync(info, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(1, ((((Dictionary)result.Data)["channels"] as Dictionary)["presence-multiple-state-channel-async-3"] as Dictionary)["user_count"]); - } - - [Test] - public void It_should_return_the_state_asynchronously_When_given_a_channel_name_that_exists_and_no_info_object_is_provided() - { - var reset = new AutoResetEvent(false); - - var channelName = "presence-multiple-state-channel-async-3"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - IGetResult result = null; - - pusherServer.FetchStateForChannelsAsync(getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - [Test] - public void It_should_not_return_the_state_asynchronously_based_When_given_a_channel_name_that_exists_an_bad_attributes() - { - AutoResetEvent reset = new AutoResetEvent(false); - - string channelName = "presence-multiple-state-channel-async-4"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var info = new { info = "does-not-exist" }; - - IGetResult result = null; - - pusherServer.FetchStateForChannelsAsync(info, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode); - StringAssert.IsMatch("info should be a comma separated list of attributes", (result as GetResult).OriginalContent); - } - } -} diff --git a/PusherServer.Tests/AcceptanceTests/Get.cs b/PusherServer.Tests/AcceptanceTests/Get.cs deleted file mode 100644 index 480eb0b..0000000 --- a/PusherServer.Tests/AcceptanceTests/Get.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; -using NUnit.Framework; -using System.IO; -using System.Web.Script.Serialization; - -namespace PusherServer.Tests.AcceptanceTests -{ - [TestFixture] - public class When_application_channels_are_queried - { - [TestFixtureSetUp] - public void Setup() - { - PusherClient.Pusher.Trace.Listeners.Add(new ConsoleTraceListener(true)); - } - - [Test] - public void It_should_return_a_200_response() - { - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - - IGetResult result = pusher.Get("/channels"); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - [Test] - public void It_should_be_possible_to_deserialize_the_request_result_body_as_an_object() - { - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - - IGetResult result = pusher.Get("/channels"); - - Assert.NotNull(result.Data); - } - - [Test] - public void It_should_be_possible_to_deserialize_the_a_channels_result_body_as_an_ChannelsList() - { - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - - IGetResult result = pusher.Get("/channels"); - - Assert.IsTrue(result.Data.Channels.Count >= 0); - } - } -} diff --git a/PusherServer.Tests/AcceptanceTests/PresenceChannels.cs b/PusherServer.Tests/AcceptanceTests/PresenceChannels.cs deleted file mode 100644 index 1227a1d..0000000 --- a/PusherServer.Tests/AcceptanceTests/PresenceChannels.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using NUnit.Framework; -using PusherServer.Tests.Helpers; - -namespace PusherServer.Tests.AcceptanceTests -{ - [TestFixture] - public class When_querying_the_Presence_Channel - { - [Test] - public void Should_get_a_list_of_subscribed_users_when_using_the_correct_channel_name_and_users_are_subscribed() - { - var reset = new AutoResetEvent(false); - - string channelName = "presence-test-channel-1"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var result = pusherServer.FetchUsersFromPresenceChannel(channelName); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(1, result.Data.Users.Length); - Assert.AreEqual("Mr Pusher", result.Data.Users[0].Id); - } - - [Test] - public void Should_get_an_empty_list_of_subscribed_users_when_using_the_correct_channel_name_and_no_users_are_subscribed() - { - var reset = new AutoResetEvent(false); - - string channelName = "presence-test-channel2"; - - var pusherServer = ClientServerFactory.CreateServer(); - - var result = pusherServer.FetchUsersFromPresenceChannel(channelName); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(0, result.Data.Users.Length); - } - - [Test] - public void should_return_bad_request_using_an_incorrect_channel_name_and_users_are_subscribed() - { - var reset = new AutoResetEvent(false); - - string channelName = "presence-test-channel3"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - var result = pusherServer.FetchUsersFromPresenceChannel("test-channel"); - - Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode); - } - - [Test] - public void Should_get_a_list_of_subscribed_users_asynchronously_when_using_the_correct_channel_name_and_users_are_subscribed() - { - var reset = new AutoResetEvent(false); - - string channelName = "presence-test-channel-async-1"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - IGetResult result = null; - pusherServer.FetchUsersFromPresenceChannelAsync(channelName, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(1, result.Data.Users.Length); - Assert.AreEqual("Mr Pusher", result.Data.Users[0].Id); - } - - [Test] - public void Should_get_an_empty_list_of_subscribed_users_asynchronously_when_using_the_correct_channel_name_and_no_users_are_subscribed() - { - var reset = new AutoResetEvent(false); - - string channelName = "presence-test-channel-async-2"; - - var pusherServer = ClientServerFactory.CreateServer(); - - IGetResult result = null; - pusherServer.FetchUsersFromPresenceChannelAsync(channelName, getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - Assert.AreEqual(0, result.Data.Users.Length); - } - - [Test] - public void should_return_bad_request_asynchronously_using_an_incorrect_channel_name_and_users_are_subscribed() - { - var reset = new AutoResetEvent(false); - - string channelName = "presence-test-channel-async-3"; - - var pusherServer = ClientServerFactory.CreateServer(); - var pusherClient = ClientServerFactory.CreateClient(pusherServer, reset, channelName); - - IGetResult result = null; - pusherServer.FetchUsersFromPresenceChannelAsync("test-channel-async", getResult => - { - result = getResult; - reset.Set(); - }); - - reset.WaitOne(TimeSpan.FromSeconds(30)); - - Assert.AreEqual(HttpStatusCode.BadRequest, result.StatusCode); - } - - [Test] - public void should_throw_an_exception_when_given_a_null_for_a_channel_name() - { - var pusherServer = ClientServerFactory.CreateServer(); - - ArgumentException caughtException = null; - - try - { - pusherServer.FetchUsersFromPresenceChannel(null); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - [Test] - public void should_throw_an_exception_when_given_an_empty_string_for_a_channel_name() - { - var pusherServer = ClientServerFactory.CreateServer(); - - ArgumentException caughtException = null; - - try - { - pusherServer.FetchUsersFromPresenceChannel(string.Empty); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - [Test] - public void should_throw_an_exception_when_given_a_null_for_a_channel_name_async() - { - var reset = new AutoResetEvent(false); - - var pusherServer = ClientServerFactory.CreateServer(); - - IGetResult result = null; - - ArgumentException caughtException = null; - - try - { - pusherServer.FetchUsersFromPresenceChannelAsync(null, getResult => - { - result = getResult; - reset.Set(); - }); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - [Test] - public void should_throw_an_exception_when_given_an_empty_string_for_a_channel_name_async() - { - var reset = new AutoResetEvent(false); - - var pusherServer = ClientServerFactory.CreateServer(); - - ArgumentException caughtException = null; - - IGetResult result = null; - try - { - pusherServer.FetchUsersFromPresenceChannelAsync(string.Empty, getResult => - { - result = getResult; - reset.Set(); - }); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("channelName cannot be null or empty", caughtException.Message); - } - - private class PresenceChannelMessage - { - public PresenceChannelUser[] Users { get; set; } - } - - private class PresenceChannelUser - { - public string Id { get; set; } - } - } -} diff --git a/PusherServer.Tests/AcceptanceTests/Trigger.cs b/PusherServer.Tests/AcceptanceTests/Trigger.cs deleted file mode 100644 index 12a8a39..0000000 --- a/PusherServer.Tests/AcceptanceTests/Trigger.cs +++ /dev/null @@ -1,297 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Threading; -using System.Web.Script.Serialization; -using NUnit.Framework; - -namespace PusherServer.Tests.AcceptanceTests -{ - [TestFixture] - public class When_Triggering_an_Event_on_a_single_Channel - { - IPusher _pusher; - - [TestFixtureSetUp] - public void Setup() - { - PusherClient.Pusher.Trace.Listeners.Add(new ConsoleTraceListener(true)); - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - } - - [Test] - public void It_should_return_a_200_response() - { - ITriggerResult result = _pusher.Trigger("my-channel", "my_event", new {hello = "world"}); - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - [Test] - public void It_should_return_a_200_response_async() - { - var waiting = new AutoResetEvent(false); - - ITriggerResult asyncResult = null; - _pusher.TriggerAsync("my-channel", "my_event", new {hello = "world"}, (ITriggerResult result) => - { - asyncResult = result; - waiting.Set(); - }); - - waiting.WaitOne(5000); - - Assert.AreEqual(HttpStatusCode.OK, asyncResult.StatusCode); - } - - [Test] - [Ignore("This test requires a node that support batch triggers, which isn't available on the default")] - public void it_should_expose_the_event_id() - { - ITriggerResult result = _pusher.Trigger("my-channel", "my_event", new {hello = "world"}); - Assert.IsTrue(string.IsNullOrEmpty(result.EventIds["my-channel"]) == false); - } - - [Test] - [Ignore("This test requires a node that support batch triggers, which isn't available on the default")] - public void it_should_expose_the_event_id_async() - { - var waiting = new AutoResetEvent(false); - - ITriggerResult asyncResult = null; - - _pusher.TriggerAsync("my-channel", "my_event", new {hello = "world"}, (ITriggerResult result) => - { - asyncResult = result; - - waiting.Set(); - }); - - waiting.WaitOne(5000); - - Assert.IsTrue(string.IsNullOrEmpty(asyncResult.EventIds["my-channel"]) == false); - } - - [Test] - public void It_should_be_received_by_a_client() - { - string channelName = "my_channel"; - string eventName = "my_event"; - - bool eventReceived = false; - AutoResetEvent reset = new AutoResetEvent(false); - - var client = new PusherClient.Pusher(Config.AppKey); - client.Host = Config.WebSocketHost; - client.Connected += new PusherClient.ConnectedEventHandler(delegate(object sender) - { - Debug.WriteLine("connected"); - reset.Set(); - }); - - Debug.WriteLine("connecting"); - client.Connect(); - - Debug.WriteLine("waiting to connect"); - reset.WaitOne(TimeSpan.FromSeconds(5)); - - Debug.WriteLine("subscribing"); - var channel = client.Subscribe(channelName); - channel.Subscribed += new PusherClient.SubscriptionEventHandler(delegate(object s) - { - Debug.WriteLine("subscribed"); - reset.Set(); - }); - - Debug.WriteLine("waiting for Subscribed"); - reset.WaitOne(TimeSpan.FromSeconds(5)); - - Debug.WriteLine("binding"); - channel.Bind(eventName, delegate(dynamic data) - { - Debug.WriteLine("event received"); - eventReceived = true; - reset.Set(); - }); - - Debug.WriteLine("Bound. Triggering"); - _pusher.Trigger(channelName, eventName, new {hello = "world"}); - - Debug.WriteLine("waiting for event to be received"); - reset.WaitOne(TimeSpan.FromSeconds(10)); - - Assert.IsTrue(eventReceived); - } - - [Test] - public void it_can_trigger_an_event_with_a_percent_in_the_message() - { - var eventJSON = File.ReadAllText("AcceptanceTests/percent-message.json"); - var message = new JavaScriptSerializer().Deserialize(eventJSON, typeof (object)); - - ITriggerResult result = _pusher.Trigger("my-channel", "my_event", message); - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - } - - [TestFixture] - public class When_Triggering_an_Event_on_a_multiple_Channels - { - [Test] - public void It_should_return_a_200_response() - { - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - ITriggerResult result = pusher.Trigger(new string[] {"my-channel-1", "my-channel-2"}, "my_event", - new {hello = "world"}); - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - [Test] - public void It_should_return_a_200_response_async() - { - var waiting = new AutoResetEvent(false); - - ITriggerResult asyncResult = null; - - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - pusher.TriggerAsync(new string[] {"my-channel-1", "my-channel-2"}, "my_event", new {hello = "world"}, - (ITriggerResult result) => - { - asyncResult = result; - - waiting.Set(); - }); - - waiting.WaitOne(5000); - - Assert.AreEqual(HttpStatusCode.OK, asyncResult.StatusCode); - } - } - - [TestFixture] - public class When_Triggering_a_Batch_of_Events - { - [Test] - public void It_should_return_a_200_response() - { - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - - var events = new Event[] - { - new Event - { - Channel = "my-channel-1", - EventName = "my_event", - Data = new {hello = "world"} - }, - new Event - { - Channel = "my-channel-2", - EventName = "my_other_event", - Data = new {hello = "other worlds"} - }, - }; - - ITriggerResult result = pusher.Trigger(events); - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - [Test] - public void It_should_return_a_200_response_async() - { - var waiting = new AutoResetEvent(false); - - ITriggerResult asyncResult = null; - - IPusher pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - HostName = Config.HttpHost - }); - - var events = new Event[] - { - new Event - { - Channel = "my-channel-1", - EventName = "my_event", - Data = new {hello = "world"} - }, - new Event - { - Channel = "my-channel-2", - EventName = "my_other_event", - Data = new {hello = "other worlds"} - }, - }; - - pusher.TriggerAsync(events, (ITriggerResult result) => - { - asyncResult = result; - waiting.Set(); - }); - - waiting.WaitOne(5000); - - Assert.AreEqual(HttpStatusCode.OK, asyncResult.StatusCode); - } - } - - [TestFixture] - public class When_Triggering_an_Event_over_HTTPS - { - IPusher _pusher = null; - - [TestFixtureSetUp] - public void Setup() - { - PusherClient.Pusher.Trace.Listeners.Add(new ConsoleTraceListener(true)); - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, new PusherOptions() - { - Encrypted = true, - HostName = Config.HttpHost - }); - } - - [Test] - public void It_should_return_a_200_response() - { - ITriggerResult result = _pusher.Trigger("my-channel", "my_event", new {hello = "world"}); - Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); - } - - [Test] - [Ignore("This test requires a node that support batch triggers, which isn't available on the default")] - public void It_should_expose_a_single_event_id_when_publishing_to_a_single_channel() - { - ITriggerResult result = _pusher.Trigger("ch1", "my_event", new {hello = "world"}); - Assert.IsTrue(result.EventIds.ContainsKey("ch1")); - Assert.AreEqual(1, result.EventIds.Count); - } - - [Test] - [Ignore("This test requires a node that support batch triggers, which isn't available on the default")] - public void It_should_expose_a_multiple_event_ids_when_publishing_to_multiple_channels() - { - var channels = new string[] {"ch1", "ch2", "ch3"}; - ITriggerResult result = _pusher.Trigger(channels, "my_event", new {hello = "world"}); - Assert.IsTrue(result.EventIds.ContainsKey("ch1")); - Assert.IsTrue(result.EventIds.ContainsKey("ch2")); - Assert.IsTrue(result.EventIds.ContainsKey("ch3")); - Assert.AreEqual(channels.Length, result.EventIds.Count); - } - } -} \ No newline at end of file diff --git a/PusherServer.Tests/AcceptanceTests/percent-message.json b/PusherServer.Tests/AcceptanceTests/percent-message.json deleted file mode 100644 index 6438fe6..0000000 --- a/PusherServer.Tests/AcceptanceTests/percent-message.json +++ /dev/null @@ -1 +0,0 @@ -{"ta":{"kttg":[{"id":"1019","n":"Kicks at goal","d":"Convert 75% of kicks at goal","v":100.0,"tv":75.0,"miv":0,"mav":100},{"id":"1151","n":"Running Metres","d":"Run more than 4 metres per carry on average","v":2.9,"tv":4.0,"miv":0,"mav":10}]}} \ No newline at end of file diff --git a/PusherServer.Tests/App.config b/PusherServer.Tests/App.config deleted file mode 100644 index 337d0ae..0000000 --- a/PusherServer.Tests/App.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/PusherServer.Tests/Config.cs b/PusherServer.Tests/Config.cs deleted file mode 100644 index 2449209..0000000 --- a/PusherServer.Tests/Config.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Configuration; - -namespace PusherServer.Tests -{ - internal static class Config - { - private const string PUSHER_APP_ID = "PUSHER_APP_ID"; - private const string PUSHER_APP_KEY = "PUSHER_APP_KEY"; - private const string PUSHER_APP_SECRET = "PUSHER_APP_SECRET"; - private const string PUSHER_HTTP_HOST = "PUSHER_APP_HOST"; - private const string PUSHER_WEBSOCKET_HOST = "PUSHER_APP_WEB_SOCKET_HOST"; - - private static string _appId; - private static string _appKey; - private static string _appSecret; - private static string _httpHost; - private static string _websocketHost; - - static Config() - { - _appId = Environment.GetEnvironmentVariable(PUSHER_APP_ID); - if (string.IsNullOrEmpty(_appId)) - { - _appId = ConfigurationManager.AppSettings.Get(PUSHER_APP_ID); - } - - _appKey = Environment.GetEnvironmentVariable(PUSHER_APP_KEY); - if (string.IsNullOrEmpty(_appKey)) - { - _appKey = ConfigurationManager.AppSettings.Get(PUSHER_APP_KEY); - } - - _appSecret = Environment.GetEnvironmentVariable(PUSHER_APP_SECRET); - if (string.IsNullOrEmpty(_appSecret)) - { - _appSecret = ConfigurationManager.AppSettings.Get(PUSHER_APP_SECRET); - } - - _httpHost = Environment.GetEnvironmentVariable(PUSHER_HTTP_HOST); - if (string.IsNullOrEmpty(_httpHost)) - { - _httpHost = ConfigurationManager.AppSettings.Get(PUSHER_HTTP_HOST); - } - - _websocketHost = Environment.GetEnvironmentVariable(PUSHER_WEBSOCKET_HOST); - if (string.IsNullOrEmpty(_websocketHost)) - { - _websocketHost = ConfigurationManager.AppSettings.Get(PUSHER_WEBSOCKET_HOST); - } - } - - public static string AppId - { - get - { - return _appId; - } - set - { - _appId = value; - } - } - - public static string AppKey - { - get - { - return _appKey; - } - set - { - _appKey = value; - } - } - - public static string AppSecret - { - get - { - return _appSecret; - } - set - { - _appSecret = value; - } - } - - public static string HttpHost - { - get - { - return _httpHost; - } - set - { - _httpHost = value; - } - } - - public static string WebSocketHost - { - get - { - return _websocketHost; - } - set - { - _websocketHost = value; - } - } - } -} diff --git a/PusherServer.Tests/Helpers/ClientServerFactory.cs b/PusherServer.Tests/Helpers/ClientServerFactory.cs deleted file mode 100644 index a93f35c..0000000 --- a/PusherServer.Tests/Helpers/ClientServerFactory.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Threading; - -namespace PusherServer.Tests.Helpers -{ - internal sealed class ClientServerFactory - { - /// - /// Create a Pusher Client, and subscribes a user - /// - /// Server to connect to - /// The AutoReset to control the subscription by the client - /// The name of the channel to subscribe to - /// A subscribed client - public static PusherClient.Pusher CreateClient(Pusher pusherServer, AutoResetEvent reset, string channelName) - { - PusherClient.Pusher pusherClient = - new PusherClient.Pusher(Config.AppKey, new PusherClient.PusherOptions() - { - Authorizer = new InMemoryAuthorizer( - pusherServer, - new PresenceChannelData() - { - user_id = "Mr Pusher", - user_info = new { twitter_id = "@pusher" } - }) - }); - - pusherClient.Connected += delegate { reset.Set(); }; - - pusherClient.Connect(); - - reset.WaitOne(TimeSpan.FromSeconds(5)); - - var channel = pusherClient.Subscribe(channelName); - - channel.Subscribed += delegate { reset.Set(); }; - - reset.WaitOne(TimeSpan.FromSeconds(5)); - - return pusherClient; - } - - /// - /// Create a Pusher Server instance - /// - /// - public static PusherServer.Pusher CreateServer() - { - return new Pusher(Config.AppId, Config.AppKey, Config.AppSecret); - } - } -} diff --git a/PusherServer.Tests/Helpers/InMemoryAuthorizer.cs b/PusherServer.Tests/Helpers/InMemoryAuthorizer.cs deleted file mode 100644 index 033dd47..0000000 --- a/PusherServer.Tests/Helpers/InMemoryAuthorizer.cs +++ /dev/null @@ -1,36 +0,0 @@ -using PusherClient; -using System.Web.Script.Serialization; - -namespace PusherServer.Tests.Helpers -{ - internal class InMemoryAuthorizer: IAuthorizer - { - PusherServer.Pusher _pusher; - PresenceChannelData _presenceData; - - public InMemoryAuthorizer(PusherServer.Pusher pusher): - this(pusher, null) - { - } - - public InMemoryAuthorizer(PusherServer.Pusher pusher, PresenceChannelData presenceData) - { - _pusher = pusher; - _presenceData = presenceData; - } - - public string Authorize(string channelName, string socketId) - { - IAuthenticationData auth = null; - if (_presenceData != null) - { - auth = _pusher.Authenticate(channelName, socketId, _presenceData); - } - else - { - auth = _pusher.Authenticate(channelName, socketId); - } - return auth.ToJson(); - } - } -} diff --git a/PusherServer.Tests/Properties/AssemblyInfo.cs b/PusherServer.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 4c51747..0000000 --- a/PusherServer.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Pusher.Server.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Pusher.Server.Tests")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// 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)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9c93f726-e5e0-4874-9bc3-444f1a6bfe49")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PusherServer.Tests/PusherServer.Tests.csproj b/PusherServer.Tests/PusherServer.Tests.csproj deleted file mode 100644 index 7196098..0000000 --- a/PusherServer.Tests/PusherServer.Tests.csproj +++ /dev/null @@ -1,107 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {37CEF892-E818-4B9D-A144-F46FBA8F1B97} - Library - Properties - PusherServer.Tests - PusherServer.Tests - v4.0 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - False - ..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll - - - ..\packages\NSubstitute.1.4.3.0\lib\NET35\NSubstitute.dll - - - ..\packages\NUnit.2.6.2\lib\nunit.framework.dll - - - False - ..\packages\PusherClient.0.1.0\lib\net40\PusherClient.dll - - - ..\packages\RestSharp.105.0.1\lib\net4\RestSharp.dll - - - - - - - - False - ..\packages\WebSocket4Net.0.10\lib\net40\WebSocket4Net.dll - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - - {D81CCD25-73B9-45E4-96BE-5BAD950715AB} - PusherServer - - - - - - - - \ No newline at end of file diff --git a/PusherServer.Tests/UnitTests/Authenticate.cs b/PusherServer.Tests/UnitTests/Authenticate.cs deleted file mode 100644 index 805f169..0000000 --- a/PusherServer.Tests/UnitTests/Authenticate.cs +++ /dev/null @@ -1,184 +0,0 @@ -using NUnit.Framework; -using RestSharp.Serializers; -using System; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_authenticating_a_private_channel - { - IPusher _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret); - - [Test] - public void the_auth_response_is_valid() - { - string channelName = "my-channel"; - string socketId = "123.456"; - - string expectedAuthString = Config.AppKey + ":" + CreateSignedString(channelName, socketId); - - IAuthenticationData result = _pusher.Authenticate(channelName, socketId); - Assert.AreEqual(expectedAuthString, result.auth); - } - - [Test] - [ExpectedException] - public void socket_id_cannot_contain_colon_prefix() - { - _pusher.Authenticate("private-test", ":444.444"); - } - - [Test] - [ExpectedException] - public void socket_id_cannot_contain_colon_suffix() - { - _pusher.Authenticate("private-test", "444.444:"); - } - - [Test] - [ExpectedException] - public void socket_id_cannot_contain_letters_suffix() - { - _pusher.Authenticate("private-test", "444.444a"); - } - - [Test] - [ExpectedException] - public void socket_id_must_contain_a_period_point() - { - _pusher.Authenticate("private-test", "444"); - } - - [Test] - [ExpectedException] - public void socket_id_must_not_contain_newline_prefix() - { - _pusher.Authenticate("private-test", "\n444.444"); - } - - [Test] - [ExpectedException] - public void socket_id_must_not_contain_newline_suffix() - { - _pusher.Authenticate("private-test", "444.444\n"); - } - - [Test] - [ExpectedException] - public void socket_id_must_not_be_empty_string() - { - _pusher.Authenticate("private-test", string.Empty); - } - - [Test] - [ExpectedException] - public void channel_must_not_have_trailing_colon() - { - AuthWithChannelName("private-channel:"); - } - [Test] - [ExpectedException] - public void channel_name_must_not_have_leading_colon() - { - AuthWithChannelName(":private-channel"); - } - - [Test] - [ExpectedException] - public void channel_name_must_not_have_leading_colon_newline() - { - AuthWithChannelName(":\nprivate-channel"); - } - - [Test] - [ExpectedException] - public void channel_name_must_not_have_trailing_colon_newline() - { - AuthWithChannelName("private-channel\n:"); - } - - [Test] - [ExpectedException] - public void channel_names_must_not_exceed_allowed_length() - { - var channelName = new String('a', ValidationHelper.CHANNEL_NAME_MAX_LENGTH + 1); - AuthWithChannelName(channelName); - } - - private void AuthWithChannelName(string channelName) - { - _pusher.Authenticate(channelName, "123.456"); - } - - private string CreateSignedString(string channelName, string socketId) - { - // null for presence data - var stringToSign = socketId + ":" + channelName; - return CryptoHelper.GetHmac256(Config.AppSecret, stringToSign); - } - } - - [TestFixture] - public class When_authenticating_a_presence_channel - { - IPusher _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret); - - [Test] - [ExpectedException(typeof(ArgumentNullException))] - public void null_presence_data_throw_Exception() - { - string channelName = "my-channel"; - string socketId = "some_socket_id"; - - PresenceChannelData data = null; - _pusher.Authenticate(channelName, socketId, data); - } - - [Test] - public void the_auth_response_is_valid() - { - string channelName = "my-channel"; - string socketId = "123.456"; - - var serializer = new JsonSerializer(); - - PresenceChannelData data = new PresenceChannelData() - { - user_id = "unique_user_id", - user_info = new { twitter_id = "@leggetter" } - }; - string presenceJSON = serializer.Serialize(data); - - string expectedAuthString = Config.AppKey + ":" + CreateSignedString(channelName, socketId, presenceJSON); - - IAuthenticationData result = _pusher.Authenticate(channelName, socketId, data); - Assert.AreEqual(expectedAuthString, result.auth); - } - - [Test] - public void channel_data_is_encoded_as_JSON() - { - string channelName = "my-channel"; - string socketId = "123.456"; - - var serializer = new JsonSerializer(); - - PresenceChannelData data = new PresenceChannelData() - { - user_id = "unique_user_id", - user_info = new { twitter_id = "@leggetter" } - }; - - string expectedChannelData = serializer.Serialize(data); ; - - IAuthenticationData result = _pusher.Authenticate(channelName, socketId, data); - Assert.AreEqual(expectedChannelData, result.channel_data); - } - - private string CreateSignedString(string channelName, string socketId, string presenceJSON) - { - var stringToSign = socketId + ":" + channelName + ":" + presenceJSON; - return CryptoHelper.GetHmac256(Config.AppSecret, stringToSign); - } - } -} diff --git a/PusherServer.Tests/UnitTests/ChannelsList.cs b/PusherServer.Tests/UnitTests/ChannelsList.cs deleted file mode 100644 index 4fd183d..0000000 --- a/PusherServer.Tests/UnitTests/ChannelsList.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NUnit.Framework; -using System.Web.Script.Serialization; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_deserializing_to_a_ChannelsList - { - string json = "{\"channels\":" + - "{" + - "\"test_channel\": {}," + - "\"presence-channel\": { \"user_count\": \"300\" } " + - "}" + - "}"; - - [Test] - public void It_should_make_the_channels_accessible_by_name_via_the_indexer() - { - var serializer = new JavaScriptSerializer(); - ChannelsList list = serializer.Deserialize(json); - - Assert.IsNotNull(list["test_channel"]); - } - - [Test] - public void It_should_be_possible_to_access_channel_information() - { - var serializer = new JavaScriptSerializer(); - ChannelsList list = serializer.Deserialize(json); - - Assert.AreEqual( list["presence-channel"]["user_count"], "300" ); - } - } -} diff --git a/PusherServer.Tests/UnitTests/Get.cs b/PusherServer.Tests/UnitTests/Get.cs deleted file mode 100644 index edbb758..0000000 --- a/PusherServer.Tests/UnitTests/Get.cs +++ /dev/null @@ -1,67 +0,0 @@ -using NSubstitute; -using NUnit.Framework; -using RestSharp; -using RestSharp.Serializers; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_using_Get_to_retrieve_a_list_of_application_channels - { - IPusher _pusher; - IRestClient _subClient; - - [SetUp] - public void Setup() - { - _subClient = Substitute.For(); - IPusherOptions options = new PusherOptions() - { - RestClient = _subClient - }; - - Config.AppId = "test-app-id"; - Config.AppKey = "test-app-key"; - Config.AppSecret = "test-app-secret"; - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, options); - } - - [Test] - public void url_is_in_expected_format() - { - _pusher.Get("/channels"); - - _subClient.Received().Execute( - Arg.Is( - x => x.Resource.StartsWith("/apps/" + Config.AppId + "/channels") - ) - ); - } - - [Test] - public void GET_request_is_made() - { - _pusher.Get("/channels"); - - _subClient.Received().Execute( - Arg.Is( - x => x.Method == Method.GET - ) - ); - } - - [Test] - public void additional_parameters_should_be_added_to_query_string() - { - _pusher.Get("/channels", new { filter_by_prefix = "presence-", info = "user_count" }); - - _subClient.Received().Execute( - Arg.Is( - x => x.Resource.Contains("&filter_by_prefix=presence-") && - x.Resource.Contains("&info=user_count") - ) - ); - } - } -} diff --git a/PusherServer.Tests/UnitTests/GetResult.cs b/PusherServer.Tests/UnitTests/GetResult.cs deleted file mode 100644 index 0e8adf4..0000000 --- a/PusherServer.Tests/UnitTests/GetResult.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using NSubstitute; -using NUnit.Framework; -using RestSharp; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_using_GetResult_to_deserialise_a_rest_response - { - readonly DefaultDeserializer _defaultDeserializer = new DefaultDeserializer(); - - [Test] - public void GetResult_should_gracefully_handle_a_deserialisation_exception() - { - var _stubRestResponse = Substitute.For(); - _stubRestResponse.Content.Returns("not json"); - _stubRestResponse.StatusCode.Returns(HttpStatusCode.BadRequest); - - var getResult = new GetResult(_stubRestResponse, _defaultDeserializer); - - Assert.AreEqual(HttpStatusCode.BadRequest, getResult.StatusCode); - StringAssert.IsMatch("not json", getResult.OriginalContent); - StringAssert.IsMatch("The HTTP response could not be deserialized to the expected type. The following exception occurred: ", getResult.Body); - Assert.AreEqual(_stubRestResponse, getResult.Response); - } - - [Test] - public void GetResult_should_deserialise_a_valid_json_response() - { - var _stubRestResponse = Substitute.For(); - _stubRestResponse.Content.Returns("[\"string1\", \"string2\", \"string3\"]"); - - var getResult = new GetResult>(_stubRestResponse, _defaultDeserializer); - - Assert.AreNotEqual(HttpStatusCode.BadRequest, getResult.StatusCode); - Assert.AreEqual(3, getResult.Data.Count); - StringAssert.IsMatch("string2", getResult.Data[1]); - Assert.AreEqual(_stubRestResponse, getResult.Response); - } - } -} diff --git a/PusherServer.Tests/UnitTests/Pusher.cs b/PusherServer.Tests/UnitTests/Pusher.cs deleted file mode 100644 index 1b0d754..0000000 --- a/PusherServer.Tests/UnitTests/Pusher.cs +++ /dev/null @@ -1,51 +0,0 @@ -using NUnit.Framework; -using System; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_creating_a_new_Pusher_instance - { - [Test] - [ExpectedException(typeof(ArgumentException))] - public void appId_cannot_be_null() - { - new Pusher(null, "app-key", "app-secret"); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void appKey_cannot_be_null() - { - new Pusher("app-id", null, "app-secret"); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void appSecret_cannot_be_null() - { - new Pusher("app-id", "app-key", null); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void appId_cannot_be_empty_string() - { - new Pusher(string.Empty, "app-key", "app-secret"); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void appKey_cannot_empty_string() - { - new Pusher("app-id", string.Empty, "app-secret"); - } - - [Test] - [ExpectedException(typeof(ArgumentException))] - public void appSecret_cannot_be_empty_string() - { - new Pusher("app-id", "app-key", string.Empty); - } - } -} \ No newline at end of file diff --git a/PusherServer.Tests/UnitTests/PusherOptions.cs b/PusherServer.Tests/UnitTests/PusherOptions.cs deleted file mode 100644 index 9cd1ac4..0000000 --- a/PusherServer.Tests/UnitTests/PusherOptions.cs +++ /dev/null @@ -1,221 +0,0 @@ -using NSubstitute; -using NUnit.Framework; -using System; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_creating_a_new_PusherOptions_instance - { - [Test] - public void a_default_RestClient_should_be_used_if_one_is_not_set_on_PusherOptions_parameter() - { - var options = new PusherOptions(); - Assert.IsNotNull(options.RestClient); - } - - [Test] - public void Port_defaults_to_80() - { - var options = new PusherOptions(); - Assert.AreEqual(80, options.Port); - } - - [Test] - public void when_Encrypted_option_is_set_Port_is_changed_to_443() - { - var options = new PusherOptions() { Encrypted = true }; - Assert.AreEqual(443, options.Port); - } - - [Test] - public void when_Encrypted_option_is_set_Port_is_changed_to_443_unless_Port_has_already_been_modified() - { - var options = new PusherOptions() { Port = 90 }; - options.Encrypted = true; - Assert.AreEqual(90, options.Port); - } - - [Test] - public void the_default_options_should_be_used_to_create_the_base_url_when_no_settings_are_changed() - { - var options = new PusherOptions(); - - StringAssert.IsMatch("http://api.pusherapp.com", options.GetBaseUrl().AbsoluteUri); - } - - [Test] - public void the_default_cluster_is_null() - { - var options = new PusherOptions(); - - Assert.AreEqual(null, options.Cluster); - } - - [Test] - public void the_default_encrypted_options_should_be_used_to_create_the_base_url_when_encrypted_is_true() - { - var options = new PusherOptions(); - options.Encrypted = true; - - StringAssert.IsMatch("https://api.pusherapp.com", options.GetBaseUrl().AbsoluteUri); - } - - [Test] - public void the_new_cluster_should_be_used_to_create_the_base_url() - { - var options= new PusherOptions(); - options.Cluster = "eu"; - - StringAssert.IsMatch("http://api-eu.pusher.com", options.GetBaseUrl().AbsoluteUri); - } - - [Test] - public void the_new_port_should_be_used_to_create_the_base_url() - { - var options = new PusherOptions(); - options.Port = 100; - - StringAssert.IsMatch("http://api.pusherapp.com:100", options.GetBaseUrl().AbsoluteUri); - } - - [Test] - public void the_new_port_should_be_used_to_create_the_base_url_when_its_encrypted() - { - var options = new PusherOptions(); - options.Encrypted = true; - options.Port = 100; - - StringAssert.IsMatch("https://api.pusherapp.com:100", options.GetBaseUrl().AbsoluteUri); - } - - [Test] - public void the_new_cluster_should_be_used_to_create_the_base_url_when_its_encrypted_and_has_a_custom_port() - { - var options = new PusherOptions(); - options.Encrypted = true; - options.Cluster = "eu"; - options.Port = 100; - - StringAssert.IsMatch("https://api-eu.pusher.com:100", options.GetBaseUrl().AbsoluteUri); - } - - [Test] - public void the_cluster_should_be_ignored_when_host_name_is_set_first() - { - var options = new PusherOptions(); - options.HostName = "api.my.domain.com"; - options.Cluster = "eu"; - - StringAssert.IsMatch("http://api.my.domain.com", options.GetBaseUrl().AbsoluteUri); - Assert.AreEqual(null, options.Cluster); - } - - [Test] - public void the_cluster_should_be_ignored_when_host_name_is_set_after() - { - var options = new PusherOptions(); - - options.Cluster = "eu"; - StringAssert.IsMatch("http://api-eu.pusher.com", options.GetBaseUrl().AbsoluteUri); - - options.HostName = "api.my.domain.com"; - StringAssert.IsMatch("http://api.my.domain.com", options.GetBaseUrl().AbsoluteUri); - Assert.AreEqual(null, options.Cluster); - } - - [Test] - [ExpectedException(typeof(FormatException))] - public void https_scheme_is_not_allowed_when_setting_host() - { - var httpsOptions = new PusherOptions(); - httpsOptions.HostName = "https://api.pusherapp.com"; - } - - [Test] - [ExpectedException(typeof(FormatException))] - public void http_scheme_is_not_allowed_when_setting_host() - { - var httpsOptions = new PusherOptions(); - httpsOptions.HostName = "http://api.pusherapp.com"; - } - - [Test] - [ExpectedException(typeof(FormatException))] - public void ftp_scheme_is_not_allowed_when_setting_host() - { - var httpsOptions = new PusherOptions(); - httpsOptions.HostName = "ftp://api.pusherapp.com"; - } - - [Test] - public void the_json_deserialiser_should_be_the_default_one_when_none_is_set() - { - var options = new PusherOptions(); - - Assert.IsInstanceOf(options.JsonDeserializer); - } - - [Test] - public void the_json_deserialiser_should_be_the_supplied_one_when_set() - { - var options = new PusherOptions(); - options.JsonDeserializer = new FakeDeserialiser(); - - Assert.IsInstanceOf(options.JsonDeserializer); - } - - [Test] - public void the_json_deserialiser_should_be_the_supplied_one_when_set_with_a_custom_and_the_set_to_null() - { - var options = new PusherOptions(); - options.JsonDeserializer = new FakeDeserialiser(); - options.JsonDeserializer = null; - - Assert.IsInstanceOf(options.JsonDeserializer); - } - - [Test] - public void the_json_serialiser_should_be_the_default_one_when_none_is_set() - { - var options = new PusherOptions(); - - Assert.IsInstanceOf(options.JsonSerializer); - } - - [Test] - public void the_json_serialiser_should_be_the_supplied_one_when_set() - { - var options = new PusherOptions(); - options.JsonSerializer = new FakeSerialiser(); - - Assert.IsInstanceOf(options.JsonSerializer); - } - - [Test] - public void the_json_serialiser_should_be_the_default_one_when_set_with_a_custom_and_the_set_to_null() - { - var options = new PusherOptions(); - options.JsonSerializer = new FakeSerialiser(); - options.JsonSerializer = null; - - Assert.IsInstanceOf(options.JsonSerializer); - } - - private class FakeDeserialiser : IDeserializeJsonStrings - { - public T Deserialize(string stringToDeserialize) - { - throw new NotImplementedException(); - } - } - - private class FakeSerialiser : ISerializeObjectsToJson - { - public string Serialize(object objectToSerialize) - { - throw new NotImplementedException(); - } - } - } -} diff --git a/PusherServer.Tests/UnitTests/RawBodySerializer.cs b/PusherServer.Tests/UnitTests/RawBodySerializer.cs deleted file mode 100644 index d9f8d2e..0000000 --- a/PusherServer.Tests/UnitTests/RawBodySerializer.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using NUnit.Framework; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_using_RawBodySerializer_to_serialize_a_rest_response - { - [Test] - public void RawBodySerialiser_should_return_an_empty_string_when_given_a_null_body() - { - var rawBodySerializer = new RawBodySerializer(); - var response = rawBodySerializer.Serialize(null); - - Assert.IsEmpty(response); - } - - [Test] - public void RawBodySerializer_should_return_the_pass_in_string_when_it_is_populated() - { - var populatedString = "populated string"; - - var rawBodySerializer = new RawBodySerializer(); - var response = rawBodySerializer.Serialize(populatedString); - - StringAssert.IsMatch(populatedString, response); - } - - [Test] - public void RawBodySerializer_should_throw_an_exception_when_given_any_object_instead_of_a_string_to_serialize() - { - var anObject = new object(); - ArgumentException caughtException = null; - - try - { - var rawBodySerializer = new RawBodySerializer(); - rawBodySerializer.Serialize(anObject); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - Assert.IsNotNull(caughtException); - StringAssert.IsMatch("The RawBodySerializer only supports strings for messages. The body type was: ", caughtException.Message); - } - } -} diff --git a/PusherServer.Tests/UnitTests/Trigger.cs b/PusherServer.Tests/UnitTests/Trigger.cs deleted file mode 100644 index 9b638d2..0000000 --- a/PusherServer.Tests/UnitTests/Trigger.cs +++ /dev/null @@ -1,459 +0,0 @@ -using System; -using System.Net; -using NSubstitute; -using NUnit.Framework; -using RestSharp; -using RestSharp.Serializers; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class When_Triggering_an_Event - { - IPusher _pusher; - IRestClient _subClient; - - string channelName = "my-channel"; - string eventName = "my_event"; - object eventData = new { hello = "world" }; - Action callback = (Action)((ITriggerResult result) => { }); - - private IRestResponse V7_PROTOCOL_SUCCESSFUL_RESPONSE; - private IRestResponse V8_PROTOCOL_SUCCESSFUL_RESPONSE; - - [TestFixtureSetUp] - public void FixtureSetUp() - { - V7_PROTOCOL_SUCCESSFUL_RESPONSE = Substitute.For(); - V7_PROTOCOL_SUCCESSFUL_RESPONSE.Content = "{}"; - V7_PROTOCOL_SUCCESSFUL_RESPONSE.StatusCode = HttpStatusCode.OK; - - V8_PROTOCOL_SUCCESSFUL_RESPONSE = Substitute.For(); - V8_PROTOCOL_SUCCESSFUL_RESPONSE.Content = TriggerResultHelper.TRIGGER_RESPONSE_JSON; - V8_PROTOCOL_SUCCESSFUL_RESPONSE.StatusCode = HttpStatusCode.OK; - } - - [SetUp] - public void Setup() - { - _subClient = Substitute.For(); - IPusherOptions options = new PusherOptions() - { - RestClient = _subClient - }; - - Config.AppId = "test-app-id"; - Config.AppKey = "test-app-key"; - Config.AppSecret = "test-app-secret"; - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, options); - - _subClient.Execute(Arg.Any()).Returns(V8_PROTOCOL_SUCCESSFUL_RESPONSE); - } - - [Test] - public void trigger_calls_are_made_over_HTTP_by_default() - { - IPusherOptions options = new PusherOptions() - { - RestClient = _subClient - }; - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, options); - - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestScheme("http", _subClient, x) - ) - ); - } - - [Test] - public void trigger_calls_are_made_over_HTTPS_when_Encrypted_option_is_set() - { - IPusherOptions options = new PusherOptions() - { - RestClient = _subClient, - Encrypted = true - }; - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, options); - - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestScheme("https", _subClient, x) - ) - ); - } - - private bool CheckRequestScheme(string urlPrefix, IRestClient client, IRestRequest req) - { - return client.BaseUrl.Scheme.StartsWith(urlPrefix); - } - - [Test] - public void trigger_calls_are_made_over_port_443_when_Encrypted_option_is_set() - { - IPusherOptions options = new PusherOptions() - { - RestClient = _subClient, - Encrypted = true, - HostName = Config.HttpHost - }; - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, options); - - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => _CheckRequestPort(443, _subClient, x) - ) - ); - } - - [Test] - public void trigger_calls_are_made_over_configured_Port_when_option_is_set() - { - int expectedPort = 900; - IPusherOptions options = new PusherOptions() - { - RestClient = _subClient, - Port = expectedPort - }; - - _pusher = new Pusher(Config.AppId, Config.AppKey, Config.AppSecret, options); - - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => _CheckRequestPort(expectedPort, _subClient, x) - ) - ); - } - - private bool _CheckRequestPort(int port, IRestClient _subClient, IRestRequest x) - { - return _subClient.BaseUrl.Port == port; - } - - [Test] - public void url_resource_is_in_expected_format() - { - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestHasExpectedUrl(x) - ) - ); - } - - private bool CheckRequestHasExpectedUrl(IRestRequest req) - { - return req.Resource.StartsWith( "/apps/" + Config.AppId + "/events?"); - } - - [Test] - public void post_payload_contains_channelName_eventName_and_eventData() - { - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestContainsPayload(x, channelName, eventName, eventData) - ) - ); - } - - private bool CheckRequestContainsPayload(IRestRequest request, string channelName, string eventName, object eventData) - { - var serializer = new JsonSerializer(); - var expectedBody = new TriggerBody() { - name = eventName, - channels = new string[]{channelName}, - data = serializer.Serialize(eventData) - }; - - var expected = serializer.Serialize(expectedBody); - - return request.Parameters[0].Type == ParameterType.RequestBody && - request.Parameters[0].ToString().Contains( expected ); - } - - [Test] - public void with_async_and_a_single_channel_the_request_is_made() - { - _pusher.TriggerAsync(channelName, eventName, eventData, callback); - - _subClient.Received().ExecuteAsync( - Arg.Any(), - Arg.Any>()); - } - - [Test] - public void with_async_and_a_single_channel_and_trigger_options_the_request_is_made() - { - _pusher.TriggerAsync(channelName, eventName, eventData, new TriggerOptions(), callback); - - _subClient.Received().ExecuteAsync( - Arg.Any(), - Arg.Any>()); - } - - [Test] - public void with_async_and_multiple_channels_the_request_is_made() - { - _pusher.TriggerAsync(new string[] { "fish", "pie" }, eventName, eventData, callback); - - _subClient.Received().ExecuteAsync( - Arg.Any(), - Arg.Any>()); - } - - [Test] - public void with_async_and_multiple_channels_and_trigger_options_the_request_is_made() - { - _pusher.TriggerAsync(new string[] { "fish", "pie" }, eventName, eventData, new TriggerOptions(), callback); - - _subClient.Received().ExecuteAsync( - Arg.Any(), - Arg.Any>()); - } - - [Test] - public void on_a_single_channel_the_socket_id_parameter_should_be_present_in_the_querystring() - { - var expectedSocketId = "123.098"; - - _pusher.Trigger(channelName, eventName, eventData, new TriggerOptions() - { - SocketId = expectedSocketId - }); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestContainsSocketIdParameter(x, expectedSocketId) - ) - ).Returns(V7_PROTOCOL_SUCCESSFUL_RESPONSE); - } - - - [Test] - public void on_a_single_channel_the_socket_id_parameter_should_be_present_in_the_querystring_async() - { - var expectedSocketId = "123.098"; - - _pusher.TriggerAsync( - channelName, - eventName, - eventData, - new TriggerOptions() - { - SocketId = expectedSocketId - }, - callback - ); - - _subClient.Received().ExecuteAsync( - Arg.Is( - x => CheckRequestContainsSocketIdParameter(x, expectedSocketId) - ), - Arg.Any>() - ); - } - - [Test] - public void on_a_multiple_channels_the_socket_id_parameter_should_be_present_in_the_querystring() - { - var expectedSocketId = "123.456"; - - _pusher.Trigger(new string[]{ "my-channel", "my-channel-2" }, eventName, eventData, new TriggerOptions() - { - SocketId = expectedSocketId - }); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestContainsSocketIdParameter(x, expectedSocketId) - ) - ); - } - - [Test] - public void on_a_multiple_channels_the_socket_id_parameter_should_be_present_in_the_querystring_async() - { - var expectedSocketId = "123.456"; - - _pusher.TriggerAsync( - new string[] { "my-channel", "my-channel-2" }, - eventName, - eventData, - new TriggerOptions() - { - SocketId = expectedSocketId - }, - callback - ); - - _subClient.Received().ExecuteAsync( - Arg.Is( - x => CheckRequestContainsSocketIdParameter(x, expectedSocketId) - ), - Arg.Any>() - ); - } - - [Test] - public void libary_name_header_is_set_with_trigger_request() - { - _pusher.Trigger(channelName, eventName, eventData); - - _subClient.Received().Execute( - Arg.Is( - x => CheckRequestContainsHeaderParameter(x, "Pusher-Library-Name", "pusher-http-dotnet") - ) - ); - } - - [Test] - [ExpectedException] - public void socket_id_cannot_contain_colon_prefix() - { - TriggerWithSocketId(":444.444"); - } - - [Test] - [ExpectedException] - public void socket_id_cannot_contain_colon_suffix() - { - TriggerWithSocketId("444.444:"); - } - - [Test] - [ExpectedException] - public void socket_id_cannot_contain_letters_suffix() - { - TriggerWithSocketId("444.444a"); - } - - [Test] - [ExpectedException] - public void socket_id_must_contain_a_period_point() - { - TriggerWithSocketId("444"); - } - - [Test] - [ExpectedException] - public void socket_id_must_not_contain_newline_prefix() - { - TriggerWithSocketId("\n444.444"); - } - - [Test] - [ExpectedException] - public void socket_id_must_not_contain_newline_suffix() - { - TriggerWithSocketId("444.444\n"); - } - - [Test] - [ExpectedException] - public void socket_id_must_not_be_empty_string() - { - TriggerWithSocketId(string.Empty); - } - - [Test] - [ExpectedException] - public void channel_must_not_have_trailing_colon() - { - TriggerWithChannelName("test_channel:"); - } - [Test] - [ExpectedException] - public void channel_name_must_not_have_leading_colon() - { - TriggerWithChannelName(":test_channel"); - } - - [Test] - [ExpectedException] - public void channel_name_must_not_have_leading_colon_newline() - { - TriggerWithChannelName(":\ntest_channel"); - } - - [Test] - [ExpectedException] - public void channel_name_must_not_have_trailing_colon_newline() - { - TriggerWithChannelName("test_channel\n:"); - } - - [Test] - [ExpectedException] - public void channel_names_in_array_must_be_validated() - { - _pusher.Trigger(new string[] { "this_one_is_okay", "test_channel\n:" }, eventName, eventData); - } - - [Test] - [ExpectedException] - public void channel_names_must_not_exceed_allowed_length() - { - var channelName = new String('a', ValidationHelper.CHANNEL_NAME_MAX_LENGTH + 1); - TriggerWithChannelName(channelName); - } - - private void TriggerWithSocketId(string socketId) - { - _pusher.Trigger(channelName, eventName, eventData, new TriggerOptions() - { - SocketId = socketId - }); - - _pusher.TriggerAsync(channelName, eventName, eventData, (ITriggerResult result) => - { - }); - } - - private void TriggerWithChannelName(string channelName) - { - _pusher.Trigger(channelName, eventName, eventData); - - _pusher.TriggerAsync(channelName, eventName, eventData, new TriggerOptions(), (ITriggerResult result) => - { - }); - } - - private static bool CheckRequestContainsSocketIdParameter(IRestRequest request, string expectedSocketId) { - var parameter = request.Parameters[0]; - return parameter.Type == ParameterType.RequestBody && - parameter.ToString().Contains("socket_id"); - } - - private static bool CheckRequestContainsHeaderParameter(IRestRequest request, string headerName, string headerValue) - { - bool found = false; - foreach(Parameter p in request.Parameters) - { - if(p.Type == ParameterType.HttpHeader && - p.Name == headerName && - p.Value.ToString() == headerValue) - { - found = true; - break; - } - } - return found; - } - } -} - diff --git a/PusherServer.Tests/UnitTests/TriggerResult.cs b/PusherServer.Tests/UnitTests/TriggerResult.cs deleted file mode 100644 index 968cdd7..0000000 --- a/PusherServer.Tests/UnitTests/TriggerResult.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Net; -using NSubstitute; -using NUnit.Framework; -using PusherServer.Exceptions; -using RestSharp; - -namespace PusherServer.Tests.UnitTests -{ - public class TriggerResultHelper - { - public static string TRIGGER_RESPONSE_JSON = "{" + - "\"event_ids\": {" + - "\"ch1\": \"ch1_event_id\"," + - "\"ch2\": \"ch2_event_id\"," + - "\"ch3\": \"ch3_event_id\"" + - "}" + - "}"; - - } - - [TestFixture] - public class When_initialisation_a_TriggerEvent - { - private IRestResponse V7_PROTOCOL_SUCCESSFUL_RESPONSE; - - [TestFixtureSetUp] - public void FixtureSetUp() - { - V7_PROTOCOL_SUCCESSFUL_RESPONSE = Substitute.For(); - V7_PROTOCOL_SUCCESSFUL_RESPONSE.Content = "{}"; - V7_PROTOCOL_SUCCESSFUL_RESPONSE.StatusCode = HttpStatusCode.OK; - } - - [Test] - [ExpectedException(typeof (ArgumentNullException))] - public void it_should_not_accept_a_null_response() - { - new TriggerResult(null); - } - - [Test] - public void it_should_treat_a_v7_protocol_200_response_as_a_successful_request() - { - var triggerResult = new TriggerResult(V7_PROTOCOL_SUCCESSFUL_RESPONSE); - Assert.AreEqual(HttpStatusCode.OK, triggerResult.StatusCode); - } - - [Test] - public void it_should_have_no_event_id_value_when_a_v7_protocol_200_response_is_returned() - { - var triggerResult = new TriggerResult(V7_PROTOCOL_SUCCESSFUL_RESPONSE); - Assert.AreEqual(0, triggerResult.EventIds.Count); - } - - [Test] - [ExpectedException(typeof (TriggerResponseException))] - public void it_should_treat_non_JSON_content_in_the_request_body_as_a_failed_request() - { - IRestResponse response = Substitute.For(); - response.Content = "FISH"; - response.StatusCode = HttpStatusCode.OK; - - new TriggerResult(response); - } - - [Test] - [ExpectedException(typeof (NotSupportedException))] - public void it_should_not_be_possible_to_change_EventIds() - { - IRestResponse response = Substitute.For(); - response.StatusCode = HttpStatusCode.OK; - response.Content = TriggerResultHelper.TRIGGER_RESPONSE_JSON; - var triggerResult = new TriggerResult(response); - - triggerResult.EventIds.Add("fish", "pie"); - } - } -} \ No newline at end of file diff --git a/PusherServer.Tests/UnitTests/WebHook.cs b/PusherServer.Tests/UnitTests/WebHook.cs deleted file mode 100644 index 1a511a6..0000000 --- a/PusherServer.Tests/UnitTests/WebHook.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using NUnit.Framework; - -namespace PusherServer.Tests.UnitTests -{ - [TestFixture] - public class when_creating_a_webhook - { - private static string GenerateValidSignature(string secret, string stringToSign) - { - return CryptoHelper.GetHmac256(secret, stringToSign); - } - - static string secret = "some_crazy_secret"; - - static string validBody = "{\"time_ms\": 1327078148132, \"events\": [{\"name\": \"channel_occupied\", \"channel\": \"test_channel\" }]}"; - static string validSignature = GenerateValidSignature(secret, validBody); - - [Test] - public void the_WebHook_will_be_valid_if_all_params_are_as_expected() - { - var webHook = new WebHook(secret, validSignature, validBody); - - Assert.IsTrue(webHook.IsValid); - } - - [Test] - public void the_event_name_can_be_retrieved_from_the_WebHook() - { - var webHook = new WebHook(secret, validSignature, validBody); - - Assert.AreEqual("channel_occupied", webHook.Events[0]["name"]); - } - - [Test] - public void the_channel_name_can_be_retrieved_from_the_WebHook() - { - var webHook = new WebHook(secret, validSignature, validBody); - - Assert.AreEqual("test_channel", webHook.Events[0]["channel"]); - } - - [Test] - public void the_WebHook_can_contain_multiple_events() - { - var body = "{\"time_ms\": 1327078148132, \"events\": " + - "[" + - "{\"name\": \"channel_occupied\", \"channel\": \"test_channel\" }," + - "{\"name\": \"channel_vacated\", \"channel\": \"test_channel2\" }" + - "]}"; - - var webHook = new WebHook(secret, validSignature, body); - Assert.AreEqual("test_channel", webHook.Events[0]["channel"]); - Assert.AreEqual("channel_occupied", webHook.Events[0]["name"]); - - Assert.AreEqual("test_channel2", webHook.Events[1]["channel"]); - Assert.AreEqual("channel_vacated", webHook.Events[1]["name"]); - } - - [Test] - public void the_WebHook_will_throw_exception_if_secret_is_null() - { - ArgumentException caughtException = null; - - try - { - new WebHook(null, validSignature, validBody); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("A secret must be provided" + Environment.NewLine + "Parameter name: secret", caughtException.Message); - } - - [Test] - public void the_WebHook_will_throw_exception_if_secret_is_empty() - { - ArgumentException caughtException = null; - - try - { - new WebHook(string.Empty, validSignature, validBody); - } - catch (ArgumentException ex) - { - caughtException = ex; - } - - StringAssert.IsMatch("A secret must be provided" + Environment.NewLine + "Parameter name: secret", caughtException.Message); - } - - [Test] - public void the_WebHook_will_be_invalid_if_they_signature_is_null() - { - var webHook = new WebHook(secret, null, validBody); - - Assert.IsFalse(webHook.IsValid); - Assert.AreEqual(2, webHook.ValidationErrors.Length); - StringAssert.IsMatch(@"The supplied signature to check was null or empty\. A signature to check must be provided\.", webHook.ValidationErrors[0]); - StringAssert.IsMatch(@"The signature did not validate\. Expected \. Got 003a63ce4da20830c4fecffb63d5b3944b64989b6458e15b26e08e244f758954", webHook.ValidationErrors[1]); - } - - [Test] - public void the_WebHook_will_be_invalid_if_they_signature_is_empty() - { - var webHook = new WebHook(secret, string.Empty, validBody); - - Assert.IsFalse(webHook.IsValid); - Assert.AreEqual(2, webHook.ValidationErrors.Length); - StringAssert.IsMatch(@"The supplied signature to check was null or empty\. A signature to check must be provided\.", webHook.ValidationErrors[0]); - StringAssert.IsMatch(@"The signature did not validate\. Expected \. Got 003a63ce4da20830c4fecffb63d5b3944b64989b6458e15b26e08e244f758954", webHook.ValidationErrors[1]); - } - - [Test] - public void the_WebHook_will_be_invalid_if_they_body_is_null() - { - var webHook = new WebHook(secret, validSignature, null); - - Assert.IsFalse(webHook.IsValid); - Assert.AreEqual(1, webHook.ValidationErrors.Length); - StringAssert.IsMatch(@"The supplied body to check was null or empty\. A body to check must be provided\.", webHook.ValidationErrors[0]); - } - - [Test] - public void the_WebHook_will_be_invalid_if_they_body_is_empty() - { - var webHook = new WebHook(secret, validSignature, string.Empty); - - Assert.IsFalse(webHook.IsValid); - Assert.AreEqual(1, webHook.ValidationErrors.Length); - StringAssert.IsMatch(@"The supplied body to check was null or empty\. A body to check must be provided\.", webHook.ValidationErrors[0]); - } - - [Test] - public void the_WebHook_will_not_be_valid_when_given_invalid_JSON_for_the_body() - { - var secret = "1c9c753dddfd049dd7f1"; - var body = "{Invalid JSON}"; - var expectedSignature = GenerateValidSignature(secret, body); - - var webHook = new WebHook(secret, expectedSignature, body); - - Assert.IsFalse(webHook.IsValid); - StringAssert.IsMatch("Exception occurred parsing the body as JSON: .*", webHook.ValidationErrors[0]); - } - - [Test] - public void the_WebHook_will_be_valid_given_alternative_values() - { - var signature = "851f492bab8f7652a2e4c82cd0212d97b4e678edf085c06bf640ed45ee7b1169"; - var secret = "1c9c753dddfd049dd7f1"; - var body = "{\"time_ms\":1423778833207,\"events\":[{\"channel\":\"test_channel\",\"name\":\"channel_occupied\"}]}"; - - var webHook = new WebHook(secret, signature, body); - Assert.IsTrue(webHook.IsValid); - } - - [Test] - public void the_WebHook_time_in_ms_is_correctly_parsed() - { - var fakeMillis = "1423850522000"; - var expectedDate = new DateTime(2015, 2, 13, 18, 2, 2, DateTimeKind.Utc); - var secret = "1c9c753dddfd049dd7f1"; - var body = "{\"time_ms\":" + fakeMillis + ",\"events\":[{\"channel\":\"test_channel\",\"name\":\"channel_occupied\"}]}"; - var expectedSignature = GenerateValidSignature(secret, body); - - var webHook = new WebHook(secret, expectedSignature, body); - Assert.AreEqual(expectedDate, webHook.Time); - } - } -} diff --git a/PusherServer.Tests/packages.config b/PusherServer.Tests/packages.config deleted file mode 100644 index c817d11..0000000 --- a/PusherServer.Tests/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/PusherServer/Properties/AssemblyInfo.cs b/PusherServer/Properties/AssemblyInfo.cs deleted file mode 100644 index a748470..0000000 --- a/PusherServer/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("PusherServer")] -[assembly: AssemblyDescription("Pusher .NET library for interacting with the HTTP API")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Pusher")] -[assembly: AssemblyProduct("pusher-http-dotnet")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// 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)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9b17b44a-89d0-43bf-8c17-134b7cdbcef3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.0")] -[assembly: AssemblyInformationalVersion("4.0.0")] - -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("PusherServer.Tests")] \ No newline at end of file diff --git a/PusherServer/PusherServer.csproj b/PusherServer/PusherServer.csproj deleted file mode 100644 index 4a7af8c..0000000 --- a/PusherServer/PusherServer.csproj +++ /dev/null @@ -1,104 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {D81CCD25-73B9-45E4-96BE-5BAD950715AB} - Library - Properties - PusherServer - PusherServer - v3.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\PusherServer.XML - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\PusherServer.XML - - - - False - ..\packages\RestSharp.105.0.1\lib\net35\RestSharp.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/PusherServer/PusherServer.nuspec b/PusherServer/PusherServer.nuspec deleted file mode 100644 index 41875e6..0000000 --- a/PusherServer/PusherServer.nuspec +++ /dev/null @@ -1,27 +0,0 @@ - - - - PusherServerInnov8tive - 4.0.5 - Pusher .NET Server Library with Push Notification - Pusher, Phil Leggetter, John McLoughlin, Gray Delacluyse - Pusher - https://raw.githubusercontent.com/pusher/pusher-http-dotnet/master/LICENSE.txt - https://github.com/Innov8tiveSoftware/pusher-http-dotnet - https://pusher.com/static_logos/64x64.png - false - The .NET library for interacting with the Pusher HTTP API and Notify HTTP API -Register at https://pusher.com and use the application credentials within your app as shown below. - - Copyright 2016 - pusher realtime notify - - - - Interact with the Pusher HTTP / Notify API - - - - - - diff --git a/PusherServer/packages.config b/PusherServer/packages.config deleted file mode 100644 index 72e2de8..0000000 --- a/PusherServer/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/README.md b/README.md index daf4d17..4919867 100644 --- a/README.md +++ b/README.md @@ -1,285 +1,285 @@ -# Pusher .NET HTTP API library - -[![Build Status](https://travis-ci.org/pusher/pusher-http-dotnet.svg?branch=master)](https://travis-ci.org/pusher/pusher-http-dotnet) - -This is a .NET library for interacting with the Pusher HTTP API. - -Registering at and use the application credentials within your app as shown below. - -Comprehensive documenation can be found at . - -## Installation - -### NuGet Package -``` -Install-Package PusherServer -``` - -## How to use - -### Constructor - -```cs -var pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET); -``` - -If you created your app in a different cluster to the default cluster, specify this as follows: - -```cs - -var options = new PusherOptions(); -options.Cluster = "eu"; - -var pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET, options); -``` - -*Please Note: the `Cluster` option is overridden by `HostName` option. So, if `HostName` is set then `Cluster` will be ignored.* - -### Publishing/Triggering events - -To trigger an event on one or more channels use the trigger function. - -#### A single channel - -```cs -var result = pusher.Trigger( "channel-1", "test_event", new { message = "hello world" } ); -``` - -#### Multiple channels - -```cs -var result = pusher.Trigger( new string[]{ "channel-1", "channel-2" ], "test_event", new { message: "hello world" } ); -``` - -#### Batches - -```cs -var events = new List[]{ - new Event(){ EventName = "test_event", Channel = "channel-1", Data = "hello world" }, - new Event(){ EventName = "test_event", Channel = "channel-1", Data = "my name is bob" }, -} - -var result = pusher.Trigger(events) -``` - -### Excluding event recipients - -In order to avoid the person that triggered the event also receiving it the `trigger` function can take an optional `ITriggerOptions` parameter which has a `SocketId` property. For more informaiton see: . - -```cs -var result = pusher.Trigger(channel, event, data, new TriggerOptions() { SocketId = "1234.56" } ); -``` - -### Authenticating Private channels - -To authorise your users to access private channels on Pusher, you can use the `Authenticate` function: - -```cs -var auth = pusher.Authenticate( channelName, socketId ); -var json = auth.ToJson(); -``` - -The `json` can then be returned to the client which will then use it for validation of the subscription with Pusher. - -For more information see: - -### Authenticating Presence channels - -Using presence channels is similar to private channels, but you can specify extra data to identify that particular user: - -```cs -var channelData = new PresenceChannelData() { - user_id: "unique_user_id", - user_info: new { - name = "Phil Leggetter" - twitter_id = "@leggetter" - } -}; -var auth = pusher.Authenticate( channelName, socketId, channelData ); -var json = auth.ToJson(); -``` - -The `json` can then be returned to the client which will then use it for validation of the subscription with Pusher. - -For more information see: - -### Application State - -It is possible to query the state of your Pusher application using the generic `Pusher.Get( resource )` method and overloads. - -For full details see: - -#### List channels - -You can get a list of channels that are present within your application: - -```cs -IGetResult result = pusher.Get("/channels"); -``` - -or - -```cs -IGetResult result = pusher.FetchStateForChannels(); -``` - -You can provide additional parameters to filter the list of channels that is returned. - -```cs -IGetResult result = pusher.Get("/channels", new { filter_by_prefix = "presence-" } ); -``` - -or - -```cs -IGetResult result = pusher.FetchStateForChannels(new { filter_by_prefix = "presence-" } ); -``` - -There is also an asynchronous variation - -```cs -pusher.FetchStateForChannelsAsync((IGetResult result) => -{ -}); -``` - -#### Fetch channel information - -Retrieve information about a single channel: - -```cs -IGetResult result = pusher.Get("/channels/my_channel" ); -``` - -or - -```cs -IGetResult result = pusher.FetchStateForChannel("my_channel"); -``` - -There is also an asynchronous variation - -```cs -pusher.FetchStateForChannelAsync("my_channel", (ITriggerResult result) => -{ -}); -``` - -Retrieve information about multiple channels: - -```cs -IGetResult result = pusher.FetchStateForChannels(); -``` - -There is also an asynchronous variation - -```cs -pusher.FetchStateForChannelsAsync((ITriggerResult result) => -{ -}); -``` - -*Note: `object` has been used above because as yet there isn't a defined class that the information can be serialized on to* - -#### Fetch a list of users on a presence channel - -Retrieve a list of users that are on a presence channel: - -```cs -IGetResult result = pusher.FetchUsersFromPresence("/channels/presence-channel/users" ); -``` - -or - -```cs -IGetResult result = pusher.FetchUsersFromPresenceChannel("my_channel"); -``` - -There is also an asynchronous variation - -```cs -pusher.FetchUsersFromPresenceChannelAsync("my_channel", (ITriggerResult result) => -{ -}); -``` - -*Note: `object` has been used above because as yet there isn't a defined class that the information can be serialized on to* - -### WebHooks - -Pusher will trigger WebHooks based on the settings you have for your application. You can consume these and use them -within your application as follows. - -For more information see . - -```cs -// How you get these depends on the framework you're using - -// HTTP_X_PUSHER_SIGNATURE from HTTP Header -var receivedSignature = "value"; - -// Body of HTTP request -var receivedBody = "value"; - -var pusher = new Pusher(...); -var webHook = pusher.ProcessWebHook(receivedSignature, receivedBody); -if(webHook.IsValid) -{ - // The WebHook validated - // Dictionary[] - var events = webHook.Events; - - foreach(var webHookEvent in webHook.Events) - { - var eventType = webHookEvent["name"]; - var channelName = webHookEvent["channel"]; - - // depending on the type of event (eventType) - // there may be other values in the Dictionary - } - -} -else { - // Log the validation errors to work out what the problem is - // webHook.ValidationErrors -} -``` - -## Development Notes - -* Developed using Visual Studio Community 2013 -* The NUnit test framework is used for testing, your copy of Visual Studio needs the "NUnit test adapter" installed from Tools -> Extensions and Updates if you wish to run the test from the IDE. -* PusherServer acceptance tests depends on [PusherClient](https://github.com/pusher-community/pusher-websocket-dotnet). - -### Alternative environments - -The solution can be opened and compiled in Xamarin Studio on OSX. - -Alternatively, the solution can be built from the command line if Mono is installed. First of all, open up a terminal and navigate to the root directory of the solution. The second step is to restore the Nuget packages, which can be done with this command - -```sh -nuget restore pusher-dotnet-server.sln -``` - -and finally build the solution, now that the packages have been restored - -```sh -xbuild pusher-dotnet-server.sln -``` - -During the build, there will be a warning about a section called TestCaseManagementSettings in the GlobalSection. Please ignore this, as it is a Visual Studio specific setting. - -## Publish to NuGet - -You should be familiar with [creating and publishing NuGet packages](http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package). - -From the `pusher-dotnet-server` directory: - -1. Update `PusherServer/Properties/AssemblyInfo.cs` with new version number. -2. Check and change any info required in `PusherServer/PusherServer.nuspec`. -3. Run `package.cmd` to pack a package to deploy to NuGet. -3. Run `tools/nuget.exe push PusherServer.{VERSION}.nupkg'. - -## License - -This code is free to use under the terms of the MIT license. +# Pusher .NET HTTP API library + +[![Build Status](https://travis-ci.org/pusher/pusher-http-dotnet.svg?branch=master)](https://travis-ci.org/pusher/pusher-http-dotnet) + +This is a .NET library for interacting with the Pusher HTTP API. + +Registering at and use the application credentials within your app as shown below. + +Comprehensive documenation can be found at . + +## Installation + +### NuGet Package +``` +Install-Package PusherServer +``` + +## How to use + +### Constructor + +```cs +var pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET); +``` + +If you created your app in a different cluster to the default cluster, specify this as follows: + +```cs + +var options = new PusherOptions(); +options.Cluster = "eu"; + +var pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET, options); +``` + +*Please Note: the `Cluster` option is overridden by `HostName` option. So, if `HostName` is set then `Cluster` will be ignored.* + +### Publishing/Triggering events + +To trigger an event on one or more channels use the trigger function. + +#### A single channel + +```cs +var result = pusher.Trigger( "channel-1", "test_event", new { message = "hello world" } ); +``` + +#### Multiple channels + +```cs +var result = pusher.Trigger( new string[]{ "channel-1", "channel-2" ], "test_event", new { message: "hello world" } ); +``` + +#### Batches + +```cs +var events = new List[]{ + new Event(){ EventName = "test_event", Channel = "channel-1", Data = "hello world" }, + new Event(){ EventName = "test_event", Channel = "channel-1", Data = "my name is bob" }, +} + +var result = pusher.Trigger(events) +``` + +### Excluding event recipients + +In order to avoid the person that triggered the event also receiving it the `trigger` function can take an optional `ITriggerOptions` parameter which has a `SocketId` property. For more informaiton see: . + +```cs +var result = pusher.Trigger(channel, event, data, new TriggerOptions() { SocketId = "1234.56" } ); +``` + +### Authenticating Private channels + +To authorise your users to access private channels on Pusher, you can use the `Authenticate` function: + +```cs +var auth = pusher.Authenticate( channelName, socketId ); +var json = auth.ToJson(); +``` + +The `json` can then be returned to the client which will then use it for validation of the subscription with Pusher. + +For more information see: + +### Authenticating Presence channels + +Using presence channels is similar to private channels, but you can specify extra data to identify that particular user: + +```cs +var channelData = new PresenceChannelData() { + user_id: "unique_user_id", + user_info: new { + name = "Phil Leggetter" + twitter_id = "@leggetter" + } +}; +var auth = pusher.Authenticate( channelName, socketId, channelData ); +var json = auth.ToJson(); +``` + +The `json` can then be returned to the client which will then use it for validation of the subscription with Pusher. + +For more information see: + +### Application State + +It is possible to query the state of your Pusher application using the generic `Pusher.Get( resource )` method and overloads. + +For full details see: + +#### List channels + +You can get a list of channels that are present within your application: + +```cs +IGetResult result = pusher.Get("/channels"); +``` + +or + +```cs +IGetResult result = pusher.FetchStateForChannels(); +``` + +You can provide additional parameters to filter the list of channels that is returned. + +```cs +IGetResult result = pusher.Get("/channels", new { filter_by_prefix = "presence-" } ); +``` + +or + +```cs +IGetResult result = pusher.FetchStateForChannels(new { filter_by_prefix = "presence-" } ); +``` + +There is also an asynchronous variation + +```cs +pusher.FetchStateForChannelsAsync((IGetResult result) => +{ +}); +``` + +#### Fetch channel information + +Retrieve information about a single channel: + +```cs +IGetResult result = pusher.Get("/channels/my_channel" ); +``` + +or + +```cs +IGetResult result = pusher.FetchStateForChannel("my_channel"); +``` + +There is also an asynchronous variation + +```cs +pusher.FetchStateForChannelAsync("my_channel", (ITriggerResult result) => +{ +}); +``` + +Retrieve information about multiple channels: + +```cs +IGetResult result = pusher.FetchStateForChannels(); +``` + +There is also an asynchronous variation + +```cs +pusher.FetchStateForChannelsAsync((ITriggerResult result) => +{ +}); +``` + +*Note: `object` has been used above because as yet there isn't a defined class that the information can be serialized on to* + +#### Fetch a list of users on a presence channel + +Retrieve a list of users that are on a presence channel: + +```cs +IGetResult result = pusher.FetchUsersFromPresence("/channels/presence-channel/users" ); +``` + +or + +```cs +IGetResult result = pusher.FetchUsersFromPresenceChannel("my_channel"); +``` + +There is also an asynchronous variation + +```cs +pusher.FetchUsersFromPresenceChannelAsync("my_channel", (ITriggerResult result) => +{ +}); +``` + +*Note: `object` has been used above because as yet there isn't a defined class that the information can be serialized on to* + +### WebHooks + +Pusher will trigger WebHooks based on the settings you have for your application. You can consume these and use them +within your application as follows. + +For more information see . + +```cs +// How you get these depends on the framework you're using + +// HTTP_X_PUSHER_SIGNATURE from HTTP Header +var receivedSignature = "value"; + +// Body of HTTP request +var receivedBody = "value"; + +var pusher = new Pusher(...); +var webHook = pusher.ProcessWebHook(receivedSignature, receivedBody); +if(webHook.IsValid) +{ + // The WebHook validated + // Dictionary[] + var events = webHook.Events; + + foreach(var webHookEvent in webHook.Events) + { + var eventType = webHookEvent["name"]; + var channelName = webHookEvent["channel"]; + + // depending on the type of event (eventType) + // there may be other values in the Dictionary + } + +} +else { + // Log the validation errors to work out what the problem is + // webHook.ValidationErrors +} +``` + +## Development Notes + +* Developed using Visual Studio Community 2013 +* The NUnit test framework is used for testing, your copy of Visual Studio needs the "NUnit test adapter" installed from Tools -> Extensions and Updates if you wish to run the test from the IDE. +* PusherServer acceptance tests depends on [PusherClient](https://github.com/pusher-community/pusher-websocket-dotnet). + +### Alternative environments + +The solution can be opened and compiled in Xamarin Studio on OSX. + +Alternatively, the solution can be built from the command line if Mono is installed. First of all, open up a terminal and navigate to the root directory of the solution. The second step is to restore the Nuget packages, which can be done with this command + +```sh +nuget restore pusher-dotnet-server.sln +``` + +and finally build the solution, now that the packages have been restored + +```sh +xbuild pusher-dotnet-server.sln +``` + +During the build, there will be a warning about a section called TestCaseManagementSettings in the GlobalSection. Please ignore this, as it is a Visual Studio specific setting. + +## Publish to NuGet + +You should be familiar with [creating and publishing NuGet packages](http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package). + +From the `pusher-dotnet-server` directory: + +1. Update `PusherServer/Properties/AssemblyInfo.cs` with new version number. +2. Check and change any info required in `PusherServer/PusherServer.nuspec`. +3. Run `package.cmd` to pack a package to deploy to NuGet. +3. Run `tools/nuget.exe push PusherServer.{VERSION}.nupkg'. + +## License + +This code is free to use under the terms of the MIT license. diff --git a/pusher-dotnet-server.sln b/pusher-dotnet-server.sln deleted file mode 100644 index c6773cc..0000000 --- a/pusher-dotnet-server.sln +++ /dev/null @@ -1,55 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{13FCFB53-1898-4AE9-B229-F917B8F52DB2}" - ProjectSection(SolutionItems) = preProject - CHANGELOG.md = CHANGELOG.md - package.cmd = package.cmd - PusherServer\PusherServer.nuspec = PusherServer\PusherServer.nuspec - README.md = README.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PusherServer", "PusherServer\PusherServer.csproj", "{D81CCD25-73B9-45E4-96BE-5BAD950715AB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PusherServer.Tests", "PusherServer.Tests\PusherServer.Tests.csproj", "{37CEF892-E818-4B9D-A144-F46FBA8F1B97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Debug|x86.ActiveCfg = Debug|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Release|Any CPU.Build.0 = Release|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {D81CCD25-73B9-45E4-96BE-5BAD950715AB}.Release|x86.ActiveCfg = Release|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Debug|x86.ActiveCfg = Debug|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Release|Any CPU.Build.0 = Release|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {37CEF892-E818-4B9D-A144-F46FBA8F1B97}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(TestCaseManagementSettings) = postSolution - CategoryFile = PusherRESTDotNet.vsmdi - EndGlobalSection -EndGlobal diff --git a/pusher-dotnetcore-server.sln b/pusher-dotnetcore-server.sln new file mode 100644 index 0000000..a22f6d3 --- /dev/null +++ b/pusher-dotnetcore-server.sln @@ -0,0 +1,34 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26403.7 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6F931361-9183-46B1-90B2-12A0BB64364F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PusherServer", "src\PusherServer\PusherServer.csproj", "{6D75B971-15AA-4CBB-A160-886A524724AD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6D75B971-15AA-4CBB-A160-886A524724AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Release|Any CPU.Build.0 = Release|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Release|x64.ActiveCfg = Release|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6D75B971-15AA-4CBB-A160-886A524724AD} = {6F931361-9183-46B1-90B2-12A0BB64364F} + EndGlobalSection +EndGlobal diff --git a/PusherServer/AuthenticationData.cs b/src/PusherServer/AuthenticationData.cs similarity index 100% rename from PusherServer/AuthenticationData.cs rename to src/PusherServer/AuthenticationData.cs diff --git a/PusherServer/BatchEvent.cs b/src/PusherServer/BatchEvent.cs similarity index 100% rename from PusherServer/BatchEvent.cs rename to src/PusherServer/BatchEvent.cs diff --git a/PusherServer/BatchTriggerBody.cs b/src/PusherServer/BatchTriggerBody.cs similarity index 100% rename from PusherServer/BatchTriggerBody.cs rename to src/PusherServer/BatchTriggerBody.cs diff --git a/PusherServer/ChannelsList.cs b/src/PusherServer/ChannelsList.cs similarity index 100% rename from PusherServer/ChannelsList.cs rename to src/PusherServer/ChannelsList.cs diff --git a/PusherServer/CryptoHelper.cs b/src/PusherServer/CryptoHelper.cs similarity index 77% rename from PusherServer/CryptoHelper.cs rename to src/PusherServer/CryptoHelper.cs index 883f708..707c59a 100644 --- a/PusherServer/CryptoHelper.cs +++ b/src/PusherServer/CryptoHelper.cs @@ -11,8 +11,11 @@ internal class CryptoHelper internal static string GetMd5Hash(string jsonData) { - var hash = new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(jsonData)); - return BytesToHex(hash); + using (var md5 = MD5.Create()) + { + var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(jsonData)); + return BytesToHex(hash); + } } private static string BytesToHex(IEnumerable byteArray) diff --git a/PusherServer/DefaultDeserializer.cs b/src/PusherServer/DefaultDeserializer.cs similarity index 70% rename from PusherServer/DefaultDeserializer.cs rename to src/PusherServer/DefaultDeserializer.cs index 96ec7f4..8214279 100644 --- a/PusherServer/DefaultDeserializer.cs +++ b/src/PusherServer/DefaultDeserializer.cs @@ -1,4 +1,4 @@ -using System.Web.Script.Serialization; +using Newtonsoft.Json; namespace PusherServer { @@ -10,7 +10,7 @@ public class DefaultDeserializer : IDeserializeJsonStrings /// public T Deserialize(string stringToDeserialize) { - return new JavaScriptSerializer().Deserialize(stringToDeserialize); + return JsonConvert.DeserializeObject(stringToDeserialize); } } } \ No newline at end of file diff --git a/PusherServer/DefaultSerializer.cs b/src/PusherServer/DefaultSerializer.cs similarity index 71% rename from PusherServer/DefaultSerializer.cs rename to src/PusherServer/DefaultSerializer.cs index 6076059..c090959 100644 --- a/PusherServer/DefaultSerializer.cs +++ b/src/PusherServer/DefaultSerializer.cs @@ -1,4 +1,4 @@ -using System.Web.Script.Serialization; +using Newtonsoft.Json; namespace PusherServer { @@ -10,7 +10,7 @@ public class DefaultSerializer : ISerializeObjectsToJson /// public string Serialize(object objectToSerialize) { - return new JavaScriptSerializer().Serialize(objectToSerialize); + return JsonConvert.SerializeObject(objectToSerialize); } } } \ No newline at end of file diff --git a/PusherServer/Event.cs b/src/PusherServer/Event.cs similarity index 100% rename from PusherServer/Event.cs rename to src/PusherServer/Event.cs diff --git a/PusherServer/EventIdData.cs b/src/PusherServer/EventIdData.cs similarity index 100% rename from PusherServer/EventIdData.cs rename to src/PusherServer/EventIdData.cs diff --git a/PusherServer/Exceptions/NotifyResponseException.cs b/src/PusherServer/Exceptions/NotifyResponseException.cs similarity index 100% rename from PusherServer/Exceptions/NotifyResponseException.cs rename to src/PusherServer/Exceptions/NotifyResponseException.cs diff --git a/PusherServer/Exceptions/TriggerResponseException.cs b/src/PusherServer/Exceptions/TriggerResponseException.cs similarity index 100% rename from PusherServer/Exceptions/TriggerResponseException.cs rename to src/PusherServer/Exceptions/TriggerResponseException.cs diff --git a/PusherServer/GetResult.cs b/src/PusherServer/GetResult.cs similarity index 100% rename from PusherServer/GetResult.cs rename to src/PusherServer/GetResult.cs diff --git a/PusherServer/IAuthenticationData.cs b/src/PusherServer/IAuthenticationData.cs similarity index 100% rename from PusherServer/IAuthenticationData.cs rename to src/PusherServer/IAuthenticationData.cs diff --git a/PusherServer/IDeserializeJsonStrings.cs b/src/PusherServer/IDeserializeJsonStrings.cs similarity index 100% rename from PusherServer/IDeserializeJsonStrings.cs rename to src/PusherServer/IDeserializeJsonStrings.cs diff --git a/PusherServer/IGetResult.cs b/src/PusherServer/IGetResult.cs similarity index 100% rename from PusherServer/IGetResult.cs rename to src/PusherServer/IGetResult.cs diff --git a/PusherServer/INotifyResult.cs b/src/PusherServer/INotifyResult.cs similarity index 100% rename from PusherServer/INotifyResult.cs rename to src/PusherServer/INotifyResult.cs diff --git a/PusherServer/IPusher.cs b/src/PusherServer/IPusher.cs similarity index 100% rename from PusherServer/IPusher.cs rename to src/PusherServer/IPusher.cs diff --git a/PusherServer/IPusherOptions.cs b/src/PusherServer/IPusherOptions.cs similarity index 98% rename from PusherServer/IPusherOptions.cs rename to src/PusherServer/IPusherOptions.cs index 7888849..baab563 100644 --- a/PusherServer/IPusherOptions.cs +++ b/src/PusherServer/IPusherOptions.cs @@ -73,5 +73,7 @@ public interface IPusherOptions /// /// The constructed URL Uri GetBaseUrl(); + + Uri GetBaseNotificationUrl(); } } diff --git a/PusherServer/IRequestResult.cs b/src/PusherServer/IRequestResult.cs similarity index 100% rename from PusherServer/IRequestResult.cs rename to src/PusherServer/IRequestResult.cs diff --git a/PusherServer/ISerializeObjectsToJson.cs b/src/PusherServer/ISerializeObjectsToJson.cs similarity index 100% rename from PusherServer/ISerializeObjectsToJson.cs rename to src/PusherServer/ISerializeObjectsToJson.cs diff --git a/PusherServer/ITriggerOptions.cs b/src/PusherServer/ITriggerOptions.cs similarity index 100% rename from PusherServer/ITriggerOptions.cs rename to src/PusherServer/ITriggerOptions.cs diff --git a/PusherServer/ITriggerResult.cs b/src/PusherServer/ITriggerResult.cs similarity index 100% rename from PusherServer/ITriggerResult.cs rename to src/PusherServer/ITriggerResult.cs diff --git a/PusherServer/IWebHook.cs b/src/PusherServer/IWebHook.cs similarity index 100% rename from PusherServer/IWebHook.cs rename to src/PusherServer/IWebHook.cs diff --git a/PusherServer/Notification.cs b/src/PusherServer/Notification.cs similarity index 95% rename from PusherServer/Notification.cs rename to src/PusherServer/Notification.cs index a462e8a..bd1370d 100644 --- a/PusherServer/Notification.cs +++ b/src/PusherServer/Notification.cs @@ -1,13 +1,12 @@ -namespace PusherServer -{ - public class Notification - { - public string title { get; set; } - public string subtitle { get; set; } - public string body { get; set; } - - public string channel { get; set; } - public string senderId { get; set; } - public string toId { get; set; } - } -} +namespace PusherServer +{ + public class Notification + { + public string title { get; set; } + public string subtitle { get; set; } + public string body { get; set; } + public string channel { get; set; } + public string senderId { get; set; } + public string toId { get; set; } + } +} diff --git a/PusherServer/NotifyBody.cs b/src/PusherServer/NotifyBody.cs similarity index 100% rename from PusherServer/NotifyBody.cs rename to src/PusherServer/NotifyBody.cs diff --git a/PusherServer/NotifyResult.cs b/src/PusherServer/NotifyResult.cs similarity index 83% rename from PusherServer/NotifyResult.cs rename to src/PusherServer/NotifyResult.cs index 1dd3074..f8e3660 100644 --- a/PusherServer/NotifyResult.cs +++ b/src/PusherServer/NotifyResult.cs @@ -1,10 +1,10 @@ -using PusherServer.Exceptions; +using Newtonsoft.Json; +using PusherServer.Exceptions; using RestSharp; using System; using System.Collections.Generic; using System.Linq; using System.Text; -using System.Web.Script.Serialization; namespace PusherServer { @@ -21,8 +21,7 @@ public NotifyResult(IRestResponse response) : base(response) NotifySubscriberData subscriberData = null; try { - JavaScriptSerializer serializer = new JavaScriptSerializer(); - subscriberData = serializer.Deserialize(response.Content); + subscriberData = JsonConvert.DeserializeObject(response.Content); } catch (Exception) { diff --git a/PusherServer/NotifySubscriberData.cs b/src/PusherServer/NotifySubscriberData.cs similarity index 100% rename from PusherServer/NotifySubscriberData.cs rename to src/PusherServer/NotifySubscriberData.cs diff --git a/PusherServer/PresenceChannelData.cs b/src/PusherServer/PresenceChannelData.cs similarity index 100% rename from PusherServer/PresenceChannelData.cs rename to src/PusherServer/PresenceChannelData.cs diff --git a/src/PusherServer/Program.cs b/src/PusherServer/Program.cs new file mode 100644 index 0000000..2461402 --- /dev/null +++ b/src/PusherServer/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; + +namespace PusherServer +{ + public class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/src/PusherServer/Properties/launchSettings.json b/src/PusherServer/Properties/launchSettings.json new file mode 100644 index 0000000..529bb94 --- /dev/null +++ b/src/PusherServer/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51894/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "PusherServer": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/PusherServer/Pusher.cs b/src/PusherServer/Pusher.cs similarity index 85% rename from PusherServer/Pusher.cs rename to src/PusherServer/Pusher.cs index 58ee578..75882c1 100644 --- a/PusherServer/Pusher.cs +++ b/src/PusherServer/Pusher.cs @@ -1,590 +1,616 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using RestSharp; -using RestSharp.Serializers; - -namespace PusherServer -{ - /// - /// Provides access to functionality within the Pusher service such as Trigger to trigger events - /// and authenticating subscription requests to private and presence channels. - /// - public class Pusher : IPusher - { - private const string ChannelUsersResource = "/channels/{0}/users"; - private const string ChannelResource = "/channels/{0}"; - private const string MultipleChannelsResource = "/channels"; - - private readonly string _appId; - private readonly string _appKey; - private readonly string _appSecret; - private readonly IPusherOptions _options; - - /// - /// Pusher library version information. - /// - public static Version VERSION - { - get - { - return Assembly.GetExecutingAssembly().GetName().Version; - } - } - - /// - /// The Pusher library name. - /// - public static String LIBRARY_NAME - { - get - { - var assembly = Assembly.GetExecutingAssembly(); - AssemblyProductAttribute adAttr = - (AssemblyProductAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyProductAttribute)); - return adAttr.Product; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The app id. - /// The app key. - /// The app secret. - public Pusher(string appId, string appKey, string appSecret) : this(appId, appKey, appSecret, null) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The app id. - /// The app key. - /// The app secret. - /// Additional options to be used with the instance e.g. setting the call to the REST API to be made over HTTPS. - public Pusher(string appId, string appKey, string appSecret, IPusherOptions options) - { - ThrowArgumentExceptionIfNullOrEmpty(appId, "appId"); - ThrowArgumentExceptionIfNullOrEmpty(appKey, "appKey"); - ThrowArgumentExceptionIfNullOrEmpty(appSecret, "appSecret"); - - if (options == null) - { - options = new PusherOptions(); - } - - _appId = appId; - _appKey = appKey; - _appSecret = appSecret; - _options = options; - } - - #region Trigger - /// - /// Triggers an event on the specified channel. - /// - /// The name of the channel the event should be triggered on. - /// The name of the event. - /// The data to be sent with the event. The event payload. - /// The result of the call to the REST API - public ITriggerResult Trigger(string channelName, string eventName, object data) - { - var channelNames = new string[] { channelName }; - return Trigger(channelNames, eventName, data); - } - - /// - /// Triggers an event on the specified channels. - /// - /// The names of the channels the event should be triggered on. - /// The name of the event. - /// The data to be sent with the event. The event payload. - /// The result of the call to the REST API - public ITriggerResult Trigger(string[] channelNames, string eventName, object data) - { - return Trigger(channelNames, eventName, data, new TriggerOptions()); - } - - /// - /// Triggers an event on the specified channel. - /// - /// The name of the channel the event should be triggered on. - /// The name of the event. - /// The data to be sent with the event. The event payload. - /// Additional options to be used when triggering the event. See . - /// The result of the call to the REST API - public ITriggerResult Trigger(string channelName, string eventName, object data, ITriggerOptions options) - { - var channelNames = new string[] { channelName }; - return Trigger(channelNames, eventName, data, options); - } - - /// - /// Triggers an event on the specified channels. - /// - /// - /// The name of the event. - /// The data to be sent with the event. The event payload. - /// Additional options to be used when triggering the event. See . - /// The result of the call to the REST API - public ITriggerResult Trigger(string[] channelNames, string eventName, object data, ITriggerOptions options) - { - - var bodyData = CreateTriggerBody(channelNames, eventName, data, options); - IRestResponse response = ExecuteTrigger("/events", bodyData); - TriggerResult result = new TriggerResult(response); - return result; - } - - /// - public ITriggerResult Trigger(Event[] events) - { - var bodyData = CreateBatchTriggerBody(events); - IRestResponse response = ExecuteTrigger("/batch_events", bodyData); - TriggerResult result = new TriggerResult(response); - return result; - } - - /// - public void TriggerAsync(string channelName, string eventName, object data, Action callback) - { - TriggerAsync(channelName, eventName, data, new TriggerOptions(), callback); - } - - /// - public void TriggerAsync(string channelName, string eventName, object data, ITriggerOptions options, Action callback) - { - TriggerAsync(new string[] { channelName }, eventName, data, options, callback); - } - - /// - public void TriggerAsync(string[] channelNames, string eventName, object data, Action callback) - { - TriggerAsync(channelNames, eventName, data, new TriggerOptions(), callback); - } - - /// - public void TriggerAsync(string[] channelNames, string eventName, object data, ITriggerOptions options, Action callback) - { - var bodyData = CreateTriggerBody(channelNames, eventName, data, options); - - ExecuteTriggerAsync("/events", bodyData, baseResponse => - { - if (callback != null) - { - callback(new TriggerResult(baseResponse)); - } - }); - } - - /// - public void TriggerAsync(Event[] events, Action callback) - { - var bodyData = CreateBatchTriggerBody(events); - - ExecuteTriggerAsync("/batch_events", bodyData, baseResponse => - { - if (callback != null) - { - callback(new TriggerResult(baseResponse)); - } - }); - } - - /// - public INotifyResult Notify(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level) - { - NotifyBody nb = new NotifyBody(); - nb.interests = interests; - nb.apns.aps.alert.body = alertText; - nb.apns.aps.alert.title = alertTitle; - nb.apns.aps.alert.subtitle = alertSubtitle; - nb.webhook_url = webhook_url; - nb.webhook_level = webhook_level.ToString(); - - IRestResponse response = ExecuteNotify("/notifications", nb); - NotifyResult result = new NotifyResult(response); - return result; - } - - /// - public INotifyResult Notify(string[] interests, Notification notification, string webhook_url, WebhookLevel webhook_level) - { - NotifyBody notifyBody = new NotifyBody(); - notifyBody.interests = interests; - notifyBody.apns.aps.notification = notification; - notifyBody.apns.aps.alert.body = "New Message"; - notifyBody.apns.aps.alert.title = notification.title; - notifyBody.apns.aps.alert.subtitle = notification.subtitle; - notifyBody.webhook_url = webhook_url; - notifyBody.webhook_level = webhook_level.ToString(); - - IRestResponse response = ExecuteNotify("/notifications", notifyBody); - NotifyResult result = new NotifyResult(response); - return result; - } - - /// - public INotifyResult Notify(string interest, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level) - { - return Notify(new string[] { interest }, alertText, alertTitle, alertSubtitle, webhook_url, webhook_level); - } - - /// - public void NotifyAsync(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level, Action callback) - { - NotifyBody nb = new NotifyBody(); - nb.interests = interests; - nb.apns.aps.alert.body = alertText; - nb.webhook_url = webhook_url; - nb.webhook_level = webhook_level.ToString(); - nb.apns.aps.alert.title = alertTitle; - nb.apns.aps.alert.subtitle = alertSubtitle; - - ExecuteNotifyAsync("/notifications", nb, baseResponse => - { - if (callback != null) - { - callback(new NotifyResult(baseResponse)); - } - }); - } - - private TriggerBody CreateTriggerBody(string[] channelNames, string eventName, object data, ITriggerOptions options) - { - ValidationHelper.ValidateChannelNames(channelNames); - ValidationHelper.ValidateSocketId(options.SocketId); - - TriggerBody bodyData = new TriggerBody() - { - name = eventName, - data = _options.JsonSerializer.Serialize(data), - channels = channelNames - }; - - if (string.IsNullOrEmpty(options.SocketId) == false) - { - bodyData.socket_id = options.SocketId; - } - - return bodyData; - } - - private BatchTriggerBody CreateBatchTriggerBody(Event[] events) - { - ValidationHelper.ValidateBatchEvents(events); - - var batchEvents = Array.ConvertAll(events, e => new BatchEvent - { - name = e.EventName, - channel = e.Channel, - socket_id = e.SocketId, - data = _options.JsonSerializer.Serialize(e.Data) - }); - - return new BatchTriggerBody() - { - batch = batchEvents - }; - } - - #endregion - - #region Authentication - /// - /// Authenticates the subscription request for a private channel. - /// - /// Name of the channel to be authenticated. - /// The socket id which uniquely identifies the connection attempting to subscribe to the channel. - /// - /// Authentication data where the required authentication token can be accessed via - /// - public IAuthenticationData Authenticate(string channelName, string socketId) - { - return new AuthenticationData(this._appKey, this._appSecret, channelName, socketId); - } - - /// - /// Authenticates the subscription request for a presence channel. - /// - /// Name of the channel to be authenticated. - /// The socket id which uniquely identifies the connection attempting to subscribe to the channel. - /// Information about the user subscribing to the presence channel. - /// If is null - /// Authentication data where the required authentication token can be accessed via - public IAuthenticationData Authenticate(string channelName, string socketId, PresenceChannelData presenceData) - { - if(presenceData == null) - { - throw new ArgumentNullException("presenceData"); - } - return new AuthenticationData(this._appKey, this._appSecret, channelName, socketId, presenceData); - } - #endregion - - #region Get - - /// - /// Using the provided response, interrogates the Pusher API - /// - /// The type of object to get - /// The name of the resource to get - /// The result of the Get - public IGetResult Get(string resource) - { - return Get(resource, null); - } - - /// - /// Using the provided response, interrogates the Pusher API - /// - /// The type of object to get - /// The name of the resource to get - /// Any additional parameters required for the Get - /// The result of the Get - public IGetResult Get(string resource, object parameters) - { - var request = CreateAuthenticatedRequest(Method.GET, resource, parameters, null); - - IRestResponse response = _options.RestClient.Execute(request); - return new GetResult(response, _options.JsonDeserializer); - } - - /// - /// Creates a new using the application secret - /// - /// The signature to use during creation - /// A JSON string representing the data to use in the Web Hook - /// A populated Web Hook - public IWebHook ProcessWebHook(string signature, string body) - { - return new WebHook(this._appSecret, signature, body); - } - - /// - public IGetResult FetchUsersFromPresenceChannel(string channelName) - { - ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); - - var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelUsersResource, channelName), null, null); - - var response = _options.RestClient.Execute(request); - - return new GetResult(response, _options.JsonDeserializer); - } - - /// - public void FetchUsersFromPresenceChannelAsync(string channelName, Action> callback) - { - ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); - - var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelUsersResource, channelName), null, null); - - _options.RestClient.ExecuteAsync(request, response => - { - callback(new GetResult(response, _options.JsonDeserializer)); - }); - } - - /// - public IGetResult FetchStateForChannel(string channelName, object info = null) - { - ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); - - var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelResource, channelName), info, null); - - var response = _options.RestClient.Execute(request); - - return new GetResult(response, _options.JsonDeserializer); - } - - /// - public void FetchStateForChannelAsync(string channelName, Action> callback) - { - FetchStateForChannelAsync(channelName, null, callback); - } - - /// - public void FetchStateForChannelAsync(string channelName, object info, Action> callback) - { - ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); - - var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelResource, channelName), info, null); - - _options.RestClient.ExecuteAsync(request, response => - { - callback(new GetResult(response, _options.JsonDeserializer)); - }); - } - - /// - public IGetResult FetchStateForChannels(object info = null) - { - var request = CreateAuthenticatedRequest(Method.GET, MultipleChannelsResource, info, null); - - var response = _options.RestClient.Execute(request); - - return new GetResult(response, _options.JsonDeserializer); - } - - /// - public void FetchStateForChannelsAsync(Action> callback) - { - FetchStateForChannelsAsync(null, callback); - } - - /// - public void FetchStateForChannelsAsync(object info, Action> callback) - { - var request = CreateAuthenticatedRequest(Method.GET, MultipleChannelsResource, info, null); - - _options.RestClient.ExecuteAsync(request, response => - { - callback(new GetResult(response, _options.JsonDeserializer)); - }); - } - - #endregion - - private IRestResponse ExecuteTrigger(string path, object requestBody) - { - _options.RestClient.BaseUrl = _options.GetBaseUrl(); - var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody); - Debug.WriteLine(string.Format("Method: {1}{0}Host: {2}{0}Resource: {3}{0}Parameters:{4}", - Environment.NewLine, - request.Method, - _options.RestClient.BaseUrl, - request.Resource, - string.Join(",", Array.ConvertAll(request.Parameters.ConvertAll(p => p.Name + "=" + p.Value).ToArray(), i => i.ToString())) - )); - - IRestResponse response = _options.RestClient.Execute(request); - - Debug.WriteLine(string.Format("Response{0}StatusCode: {1}{0}Body: {2}", - Environment.NewLine, - response.StatusCode, - response.Content)); - - return response; - } - - private void ExecuteTriggerAsync(string path, object requestBody, Action callback) - { - _options.RestClient.BaseUrl = _options.GetBaseUrl(); - - var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody); - _options.RestClient.ExecuteAsync(request, callback); - } - - private void ExecuteNotifyAsync(string path, object requestBody, Action callback) - { - _options.RestClient.BaseUrl = _options.GetBaseUrl(); - - var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody,true); - _options.RestClient.ExecuteAsync(request, callback); - } - - private IRestResponse ExecuteNotify(string path, object requestBody) - { - _options.RestClient.BaseUrl = _options.GetBaseUrl(); - var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody, true); - Debug.WriteLine(string.Format("Method: {1}{0}Host: {2}{0}Resource: {3}{0}Parameters:{4}", - Environment.NewLine, - request.Method, - _options.RestClient.BaseUrl, - request.Resource, - string.Join(",", Array.ConvertAll(request.Parameters.ConvertAll(p => p.Name + "=" + p.Value).ToArray(), i => i.ToString())) - )); - - IRestResponse response = _options.RestClient.Execute(request); - - Debug.WriteLine(string.Format("Response{0}StatusCode: {1}{0}Body: {2}", - Environment.NewLine, - response.StatusCode, - response.Content)); - - return response; - } - - private IRestRequest CreateAuthenticatedRequest(Method requestType, string resource, object requestParameters, object requestBody, bool nativeAPI = false) - { - SortedDictionary queryParams = GetObjectProperties(requestParameters); - - int timeNow = (int)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds); - queryParams.Add("auth_key", this._appKey); - queryParams.Add("auth_timestamp", timeNow.ToString()); - queryParams.Add("auth_version", "1.0"); - - if (requestBody != null) - { - JsonSerializer serializer = new JsonSerializer(); - var bodyDataJson = serializer.Serialize(requestBody); - var bodyMD5 = CryptoHelper.GetMd5Hash(bodyDataJson); - queryParams.Add("body_md5", bodyMD5); - } - - string queryString = string.Empty; - foreach(KeyValuePair parameter in queryParams) - { - queryString += parameter.Key + "=" + parameter.Value + "&"; - } - queryString = queryString.TrimEnd('&'); - - resource = resource.TrimStart('/'); - string path; - if (!nativeAPI) - { - path = string.Format("/apps/{0}/{1}", this._appId, resource); - }else - { - path = string.Format("/{0}/{1}/apps/{2}/{3}", _options.Notification_Prefix,_options.Notificaiton_Version, this._appId, resource); - } - - string authToSign = String.Format( - Enum.GetName(requestType.GetType(), requestType) + - "\n{0}\n{1}", - path, - queryString); - - var authSignature = CryptoHelper.GetHmac256(_appSecret, authToSign); - - var requestUrl = path + "?" + queryString + "&auth_signature=" + authSignature; - var request = new RestRequest(requestUrl); - request.RequestFormat = DataFormat.Json; - request.Method = requestType; - request.AddBody(requestBody); - - request.AddHeader("Pusher-Library-Name", LIBRARY_NAME); - request.AddHeader("Pusher-Library-Version", VERSION.ToString(3)); - - return request; - } - - private SortedDictionary GetObjectProperties(object obj) - { - SortedDictionary properties = new SortedDictionary(); - - if (obj != null) - { - Type objType = obj.GetType(); - IList propertyInfos = new List(objType.GetProperties()); - - foreach (PropertyInfo propertyInfo in propertyInfos) - { - properties.Add(propertyInfo.Name, propertyInfo.GetValue(obj, null).ToString()); - } - } - - return properties; - } - - private void ThrowArgumentExceptionIfNullOrEmpty(string value, string argumentName) - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException(string.Format("{0} cannot be null or empty", argumentName)); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using RestSharp; +using RestSharp.Serializers; +using Newtonsoft.Json; +using System.Threading.Tasks; + +namespace PusherServer +{ + /// + /// Provides access to functionality within the Pusher service such as Trigger to trigger events + /// and authenticating subscription requests to private and presence channels. + /// + public class Pusher : IPusher + { + private const string ChannelUsersResource = "/channels/{0}/users"; + private const string ChannelResource = "/channels/{0}"; + private const string MultipleChannelsResource = "/channels"; + + private readonly string _appId; + private readonly string _appKey; + private readonly string _appSecret; + private readonly IPusherOptions _options; + + /// + /// Pusher library version information. + /// + public static Version VERSION + { + get + { + return typeof(Pusher).GetTypeInfo().Assembly.GetName().Version; + } + } + + /// + /// The Pusher library name. + /// + public static String LIBRARY_NAME + { + get + { + //return typeof(Pusher).GetTypeInfo().GetCustomAttribute().Product; + return "Pusher"; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The app id. + /// The app key. + /// The app secret. + public Pusher(string appId, string appKey, string appSecret) : this(appId, appKey, appSecret, null) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The app id. + /// The app key. + /// The app secret. + /// Additional options to be used with the instance e.g. setting the call to the REST API to be made over HTTPS. + public Pusher(string appId, string appKey, string appSecret, IPusherOptions options) + { + ThrowArgumentExceptionIfNullOrEmpty(appId, "appId"); + ThrowArgumentExceptionIfNullOrEmpty(appKey, "appKey"); + ThrowArgumentExceptionIfNullOrEmpty(appSecret, "appSecret"); + + if (options == null) + { + options = new PusherOptions(); + } + + _appId = appId; + _appKey = appKey; + _appSecret = appSecret; + _options = options; + } + + #region Trigger + /// + /// Triggers an event on the specified channel. + /// + /// The name of the channel the event should be triggered on. + /// The name of the event. + /// The data to be sent with the event. The event payload. + /// The result of the call to the REST API + public ITriggerResult Trigger(string channelName, string eventName, object data) + { + var channelNames = new string[] { channelName }; + return Trigger(channelNames, eventName, data); + } + + /// + /// Triggers an event on the specified channels. + /// + /// The names of the channels the event should be triggered on. + /// The name of the event. + /// The data to be sent with the event. The event payload. + /// The result of the call to the REST API + public ITriggerResult Trigger(string[] channelNames, string eventName, object data) + { + return Trigger(channelNames, eventName, data, new TriggerOptions()); + } + + /// + /// Triggers an event on the specified channel. + /// + /// The name of the channel the event should be triggered on. + /// The name of the event. + /// The data to be sent with the event. The event payload. + /// Additional options to be used when triggering the event. See . + /// The result of the call to the REST API + public ITriggerResult Trigger(string channelName, string eventName, object data, ITriggerOptions options) + { + var channelNames = new string[] { channelName }; + return Trigger(channelNames, eventName, data, options); + } + + /// + /// Triggers an event on the specified channels. + /// + /// + /// The name of the event. + /// The data to be sent with the event. The event payload. + /// Additional options to be used when triggering the event. See . + /// The result of the call to the REST API + public ITriggerResult Trigger(string[] channelNames, string eventName, object data, ITriggerOptions options) + { + + var bodyData = CreateTriggerBody(channelNames, eventName, data, options); + IRestResponse response = ExecuteTrigger("/events", bodyData); + TriggerResult result = new TriggerResult(response); + return result; + } + + /// + public ITriggerResult Trigger(Event[] events) + { + var bodyData = CreateBatchTriggerBody(events); + IRestResponse response = ExecuteTrigger("/batch_events", bodyData); + TriggerResult result = new TriggerResult(response); + return result; + } + + /// + public void TriggerAsync(string channelName, string eventName, object data, Action callback) + { + TriggerAsync(channelName, eventName, data, new TriggerOptions(), callback); + } + + /// + public void TriggerAsync(string channelName, string eventName, object data, ITriggerOptions options, Action callback) + { + TriggerAsync(new string[] { channelName }, eventName, data, options, callback); + } + + /// + public void TriggerAsync(string[] channelNames, string eventName, object data, Action callback) + { + TriggerAsync(channelNames, eventName, data, new TriggerOptions(), callback); + } + + /// + public void TriggerAsync(string[] channelNames, string eventName, object data, ITriggerOptions options, Action callback) + { + var bodyData = CreateTriggerBody(channelNames, eventName, data, options); + + ExecuteTriggerAsync("/events", bodyData, baseResponse => + { + if (callback != null) + { + callback(new TriggerResult(baseResponse)); + } + }); + } + + /// + public void TriggerAsync(Event[] events, Action callback) + { + var bodyData = CreateBatchTriggerBody(events); + + ExecuteTriggerAsync("/batch_events", bodyData, baseResponse => + { + if (callback != null) + { + callback(new TriggerResult(baseResponse)); + } + }); + } + + /// + public INotifyResult Notify(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level) + { + NotifyBody nb = new NotifyBody(); + nb.interests = interests; + nb.apns.aps.alert.body = alertText; + nb.apns.aps.alert.title = alertTitle; + nb.apns.aps.alert.subtitle = alertSubtitle; + nb.webhook_url = webhook_url; + nb.webhook_level = webhook_level.ToString(); + + IRestResponse response = ExecuteNotify("/notifications", nb); + NotifyResult result = new NotifyResult(response); + return result; + } + + /// + public INotifyResult Notify(string[] interests, Notification notification, string webhook_url, WebhookLevel webhook_level) + { + NotifyBody notifyBody = new NotifyBody(); + notifyBody.interests = interests; + notifyBody.apns.aps.notification = notification; + notifyBody.apns.aps.alert.body = "New Message"; + notifyBody.apns.aps.alert.title = notification.title; + notifyBody.apns.aps.alert.subtitle = notification.subtitle; + notifyBody.webhook_url = webhook_url; + notifyBody.webhook_level = webhook_level.ToString(); + + IRestResponse response = ExecuteNotify("/notifications", notifyBody); + NotifyResult result = new NotifyResult(response); + return result; + } + + /// + public INotifyResult Notify(string interest, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level) + { + return Notify(new string[] { interest }, alertText, alertTitle, alertSubtitle, webhook_url, webhook_level); + } + + /// + public void NotifyAsync(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level, Action callback) + { + NotifyBody nb = new NotifyBody(); + nb.interests = interests; + nb.apns.aps.alert.body = alertText; + nb.webhook_url = webhook_url; + nb.webhook_level = webhook_level.ToString(); + nb.apns.aps.alert.title = alertTitle; + nb.apns.aps.alert.subtitle = alertSubtitle; + + ExecuteNotifyAsync("/notifications", nb, baseResponse => + { + if (callback != null) + { + callback(new NotifyResult(baseResponse)); + } + }); + } + + private TriggerBody CreateTriggerBody(string[] channelNames, string eventName, object data, ITriggerOptions options) + { + ValidationHelper.ValidateChannelNames(channelNames); + ValidationHelper.ValidateSocketId(options.SocketId); + + TriggerBody bodyData = new TriggerBody() + { + name = eventName, + data = _options.JsonSerializer.Serialize(data), + channels = channelNames + }; + + if (string.IsNullOrEmpty(options.SocketId) == false) + { + bodyData.socket_id = options.SocketId; + } + + return bodyData; + } + + private BatchTriggerBody CreateBatchTriggerBody(Event[] events) + { + ValidationHelper.ValidateBatchEvents(events); + + var batchEvents = new List(events.Length); + + foreach(var e in events) + { + batchEvents.Add(new BatchEvent { + name = e.EventName, + channel = e.Channel, + socket_id = e.SocketId, + data = JsonConvert.SerializeObject(e.Data) + }); + } + + return new BatchTriggerBody() + { + batch = batchEvents.ToArray() + }; + } + + #endregion + + #region Authentication + /// + /// Authenticates the subscription request for a private channel. + /// + /// Name of the channel to be authenticated. + /// The socket id which uniquely identifies the connection attempting to subscribe to the channel. + /// + /// Authentication data where the required authentication token can be accessed via + /// + public IAuthenticationData Authenticate(string channelName, string socketId) + { + return new AuthenticationData(this._appKey, this._appSecret, channelName, socketId); + } + + /// + /// Authenticates the subscription request for a presence channel. + /// + /// Name of the channel to be authenticated. + /// The socket id which uniquely identifies the connection attempting to subscribe to the channel. + /// Information about the user subscribing to the presence channel. + /// If is null + /// Authentication data where the required authentication token can be accessed via + public IAuthenticationData Authenticate(string channelName, string socketId, PresenceChannelData presenceData) + { + if(presenceData == null) + { + throw new ArgumentNullException("presenceData"); + } + return new AuthenticationData(this._appKey, this._appSecret, channelName, socketId, presenceData); + } + #endregion + + #region Get + + /// + /// Using the provided response, interrogates the Pusher API + /// + /// The type of object to get + /// The name of the resource to get + /// The result of the Get + public IGetResult Get(string resource) + { + return Get(resource, null); + } + + /// + /// Using the provided response, interrogates the Pusher API + /// + /// The type of object to get + /// The name of the resource to get + /// Any additional parameters required for the Get + /// The result of the Get + public IGetResult Get(string resource, object parameters) + { + var request = CreateAuthenticatedRequest(Method.GET, resource, parameters, null); + + var taskCompletionSource = new TaskCompletionSource(); + _options.RestClient.ExecuteAsync(request, response => + { + taskCompletionSource.SetResult(response); + }); + return new GetResult(taskCompletionSource.Task.Result, _options.JsonDeserializer); + } + + /// + /// Creates a new using the application secret + /// + /// The signature to use during creation + /// A JSON string representing the data to use in the Web Hook + /// A populated Web Hook + public IWebHook ProcessWebHook(string signature, string body) + { + return new WebHook(this._appSecret, signature, body); + } + + /// + public IGetResult FetchUsersFromPresenceChannel(string channelName) + { + ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); + + var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelUsersResource, channelName), null, null); + + var taskCompletionSource = new TaskCompletionSource(); + _options.RestClient.ExecuteAsync(request, response => + { + taskCompletionSource.SetResult(response); + }); + return new GetResult(taskCompletionSource.Task.Result, _options.JsonDeserializer); + } + + /// + public void FetchUsersFromPresenceChannelAsync(string channelName, Action> callback) + { + ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); + + var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelUsersResource, channelName), null, null); + + _options.RestClient.ExecuteAsync(request, response => + { + callback(new GetResult(response, _options.JsonDeserializer)); + }); + } + + /// + public IGetResult FetchStateForChannel(string channelName, object info = null) + { + ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); + + var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelResource, channelName), info, null); + + var taskCompletionSource = new TaskCompletionSource(); + _options.RestClient.ExecuteAsync(request, response => + { + taskCompletionSource.SetResult(response); + }); + return new GetResult(taskCompletionSource.Task.Result, _options.JsonDeserializer); + } + + /// + public void FetchStateForChannelAsync(string channelName, Action> callback) + { + FetchStateForChannelAsync(channelName, null, callback); + } + + /// + public void FetchStateForChannelAsync(string channelName, object info, Action> callback) + { + ThrowArgumentExceptionIfNullOrEmpty(channelName, "channelName"); + + var request = CreateAuthenticatedRequest(Method.GET, string.Format(ChannelResource, channelName), info, null); + + _options.RestClient.ExecuteAsync(request, response => + { + callback(new GetResult(response, _options.JsonDeserializer)); + }); + } + + /// + public IGetResult FetchStateForChannels(object info = null) + { + var request = CreateAuthenticatedRequest(Method.GET, MultipleChannelsResource, info, null); + + var taskCompletionSource = new TaskCompletionSource(); + _options.RestClient.ExecuteAsync(request, response => + { + taskCompletionSource.SetResult(response); + }); + return new GetResult(taskCompletionSource.Task.Result, _options.JsonDeserializer); + } + + /// + public void FetchStateForChannelsAsync(Action> callback) + { + FetchStateForChannelsAsync(null, callback); + } + + /// + public void FetchStateForChannelsAsync(object info, Action> callback) + { + var request = CreateAuthenticatedRequest(Method.GET, MultipleChannelsResource, info, null); + + _options.RestClient.ExecuteAsync(request, response => + { + callback(new GetResult(response, _options.JsonDeserializer)); + }); + } + + #endregion + + private IRestResponse ExecuteTrigger(string path, object requestBody) + { + _options.RestClient.BaseUrl = _options.GetBaseUrl(); + var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody); + //Debug.WriteLine(string.Format("Method: {1}{0}Host: {2}{0}Resource: {3}{0}Parameters:{4}", + // Environment.NewLine, + // request.Method, + // _options.RestClient.BaseUrl, + // request.Resource, + // string.Join(",", Array.ConvertAll(request.Parameters.ConvertAll(p => p.Name + "=" + p.Value).ToArray(), i => i.ToString())) + //)); + + var taskCompletionSource = new TaskCompletionSource(); + _options.RestClient.ExecuteAsync(request, res => + { + taskCompletionSource.SetResult(res); + }); + IRestResponse response = taskCompletionSource.Task.Result; + + Debug.WriteLine(string.Format("Response{0}StatusCode: {1}{0}Body: {2}", + Environment.NewLine, + response.StatusCode, + response.Content)); + + return response; + } + + private void ExecuteTriggerAsync(string path, object requestBody, Action callback) + { + _options.RestClient.BaseUrl = _options.GetBaseUrl(); + + var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody); + _options.RestClient.ExecuteAsync(request, callback); + } + + private void ExecuteNotifyAsync(string path, object requestBody, Action callback) + { + _options.RestClient.BaseUrl = _options.GetBaseNotificationUrl(); + + var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody,true); + _options.RestClient.ExecuteAsync(request, callback); + } + + private IRestResponse ExecuteNotify(string path, object requestBody) + { + _options.RestClient.BaseUrl = _options.GetBaseNotificationUrl(); + var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody, true); + //Debug.WriteLine(string.Format("Method: {1}{0}Host: {2}{0}Resource: {3}{0}Parameters:{4}", + // Environment.NewLine, + // request.Method, + // _options.RestClient.BaseUrl, + // request.Resource, + // string.Join(",", Array.ConvertAll(request.Parameters.ConvertAll(p => p.Name + "=" + p.Value).ToArray(), i => i.ToString())) + //)); + + var taskCompletionSource = new TaskCompletionSource(); + _options.RestClient.ExecuteAsync(request, res => + { + taskCompletionSource.SetResult(res); + }); + IRestResponse response = taskCompletionSource.Task.Result; + + Debug.WriteLine(string.Format("Response{0}StatusCode: {1}{0}Body: {2}", + Environment.NewLine, + response.StatusCode, + response.Content)); + + return response; + } + + private IRestRequest CreateAuthenticatedRequest(Method requestType, string resource, object requestParameters, object requestBody, bool nativeAPI = false) + { + SortedDictionary queryParams = GetObjectProperties(requestParameters); + + int timeNow = (int)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds); + queryParams.Add("auth_key", this._appKey); + queryParams.Add("auth_timestamp", timeNow.ToString()); + queryParams.Add("auth_version", "1.0"); + + if (requestBody != null) + { + var bodyDataJson = JsonConvert.SerializeObject(requestBody); + var bodyMD5 = CryptoHelper.GetMd5Hash(bodyDataJson); + queryParams.Add("body_md5", bodyMD5); + } + + string queryString = string.Empty; + foreach(KeyValuePair parameter in queryParams) + { + queryString += parameter.Key + "=" + parameter.Value + "&"; + } + queryString = queryString.TrimEnd('&'); + + resource = resource.TrimStart('/'); + string path; + if (!nativeAPI) + { + path = string.Format("/apps/{0}/{1}", this._appId, resource); + }else + { + path = string.Format("/{0}/{1}/apps/{2}/{3}", _options.Notification_Prefix,_options.Notificaiton_Version, this._appId, resource); + } + + string authToSign = String.Format( + Enum.GetName(requestType.GetType(), requestType) + + "\n{0}\n{1}", + path, + queryString); + + var authSignature = CryptoHelper.GetHmac256(_appSecret, authToSign); + + var requestUrl = path + "?" + queryString + "&auth_signature=" + authSignature; + var request = new RestRequest(requestUrl); + request.RequestFormat = DataFormat.Json; + request.Method = requestType; + request.AddBody(requestBody); + + request.AddHeader("Pusher-Library-Name", LIBRARY_NAME); + request.AddHeader("Pusher-Library-Version", VERSION.ToString(3)); + + return request; + } + + private SortedDictionary GetObjectProperties(object obj) + { + SortedDictionary properties = new SortedDictionary(); + + if (obj != null) + { + Type objType = obj.GetType(); + IList propertyInfos = new List(objType.GetProperties()); + + foreach (PropertyInfo propertyInfo in propertyInfos) + { + properties.Add(propertyInfo.Name, propertyInfo.GetValue(obj, null).ToString()); + } + } + + return properties; + } + + private void ThrowArgumentExceptionIfNullOrEmpty(string value, string argumentName) + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException(string.Format("{0} cannot be null or empty", argumentName)); + } + } + } +} diff --git a/PusherServer/PusherOptions.cs b/src/PusherServer/PusherOptions.cs similarity index 96% rename from PusherServer/PusherOptions.cs rename to src/PusherServer/PusherOptions.cs index ac8fe9c..7fe3242 100644 --- a/PusherServer/PusherOptions.cs +++ b/src/PusherServer/PusherOptions.cs @@ -230,6 +230,13 @@ public Uri GetBaseUrl() return new Uri(baseUrl); } + public Uri GetBaseNotificationUrl() + { + string baseUrl = (Encrypted ? "https" : "http") + "://" + DEFAULT_NOTIFICATION_REST_API_HOST; + + return new Uri(baseUrl); + } + private string GetPort() { var port = string.Empty; diff --git a/src/PusherServer/PusherServer.csproj b/src/PusherServer/PusherServer.csproj new file mode 100644 index 0000000..990add1 --- /dev/null +++ b/src/PusherServer/PusherServer.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp1.1 + true + PusherServer + Exe + PusherServer + 1.0.4 + $(PackageTargetFallback);dotnet5.6;portable-net45+win8 + false + false + false + false + false + false + false + false + + + + + + + + + + + diff --git a/PusherServer/RawBodySerializer.cs b/src/PusherServer/RawBodySerializer.cs similarity index 100% rename from PusherServer/RawBodySerializer.cs rename to src/PusherServer/RawBodySerializer.cs diff --git a/PusherServer/RequestResult.cs b/src/PusherServer/RequestResult.cs similarity index 100% rename from PusherServer/RequestResult.cs rename to src/PusherServer/RequestResult.cs diff --git a/src/PusherServer/Startup.cs b/src/PusherServer/Startup.cs new file mode 100644 index 0000000..cd0bb77 --- /dev/null +++ b/src/PusherServer/Startup.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace PusherServer +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } + } +} diff --git a/PusherServer/TriggerBody.cs b/src/PusherServer/TriggerBody.cs similarity index 100% rename from PusherServer/TriggerBody.cs rename to src/PusherServer/TriggerBody.cs diff --git a/PusherServer/TriggerOptions.cs b/src/PusherServer/TriggerOptions.cs similarity index 100% rename from PusherServer/TriggerOptions.cs rename to src/PusherServer/TriggerOptions.cs diff --git a/PusherServer/TriggerResult.cs b/src/PusherServer/TriggerResult.cs similarity index 86% rename from PusherServer/TriggerResult.cs rename to src/PusherServer/TriggerResult.cs index 1aafbb7..c8d19ba 100644 --- a/PusherServer/TriggerResult.cs +++ b/src/PusherServer/TriggerResult.cs @@ -1,10 +1,10 @@ using System; using System.Net; using System.Collections.Generic; -using System.Web.Script.Serialization; using PusherServer.Exceptions; using PusherServer.Util; using RestSharp; +using Newtonsoft.Json; namespace PusherServer { @@ -21,8 +21,7 @@ public TriggerResult(IRestResponse response) : base(response) EventIdData eventIdData = null; try { - JavaScriptSerializer serializer = new JavaScriptSerializer(); - eventIdData = serializer.Deserialize(response.Content); + eventIdData = JsonConvert.DeserializeObject(response.Content); } catch (Exception) { diff --git a/PusherServer/Util/ReadOnlyDictionary.cs b/src/PusherServer/Util/ReadOnlyDictionary.cs similarity index 100% rename from PusherServer/Util/ReadOnlyDictionary.cs rename to src/PusherServer/Util/ReadOnlyDictionary.cs diff --git a/PusherServer/ValidationHelper.cs b/src/PusherServer/ValidationHelper.cs similarity index 100% rename from PusherServer/ValidationHelper.cs rename to src/PusherServer/ValidationHelper.cs diff --git a/PusherServer/WebHook.cs b/src/PusherServer/WebHook.cs similarity index 100% rename from PusherServer/WebHook.cs rename to src/PusherServer/WebHook.cs diff --git a/PusherServer/WebHookData.cs b/src/PusherServer/WebHookData.cs similarity index 100% rename from PusherServer/WebHookData.cs rename to src/PusherServer/WebHookData.cs diff --git a/PusherServer/WebHookEvent.cs b/src/PusherServer/WebHookEvent.cs similarity index 100% rename from PusherServer/WebHookEvent.cs rename to src/PusherServer/WebHookEvent.cs diff --git a/PusherServer/WebhookLevel.cs b/src/PusherServer/WebhookLevel.cs similarity index 100% rename from PusherServer/WebhookLevel.cs rename to src/PusherServer/WebhookLevel.cs diff --git a/src/PusherServer/web.config b/src/PusherServer/web.config new file mode 100644 index 0000000..dc0514f --- /dev/null +++ b/src/PusherServer/web.config @@ -0,0 +1,14 @@ + + + + + + + + + + + +