From 9400ace65122d013d3334a7c03be8dc7a8988e0b Mon Sep 17 00:00:00 2001 From: JasonH Date: Wed, 14 Dec 2016 12:49:38 -0600 Subject: [PATCH 1/9] Add .gitignore and .gitattributes. --- .gitattributes | 63 +++++++++++++ .gitignore | 245 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore 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 new file mode 100644 index 0000000..3a2238d --- /dev/null +++ b/.gitignore @@ -0,0 +1,245 @@ +## 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 From 56cd424b99786ac51de72b7afabebece3af43a2c Mon Sep 17 00:00:00 2001 From: JasonH Date: Wed, 14 Dec 2016 12:49:41 -0600 Subject: [PATCH 2/9] Add project files. --- global.json | 6 + pusher-dotnetcore-server.sln | 32 +++ src/PusherServer/Program.cs | 24 +++ src/PusherServer/Project_Readme.html | 187 ++++++++++++++++++ .../Properties/launchSettings.json | 27 +++ src/PusherServer/PusherServer.xproj | 25 +++ src/PusherServer/Startup.cs | 37 ++++ src/PusherServer/project.json | 48 +++++ src/PusherServer/web.config | 14 ++ 9 files changed, 400 insertions(+) create mode 100644 global.json create mode 100644 pusher-dotnetcore-server.sln create mode 100644 src/PusherServer/Program.cs create mode 100644 src/PusherServer/Project_Readme.html create mode 100644 src/PusherServer/Properties/launchSettings.json create mode 100644 src/PusherServer/PusherServer.xproj create mode 100644 src/PusherServer/Startup.cs create mode 100644 src/PusherServer/project.json create mode 100644 src/PusherServer/web.config diff --git a/global.json b/global.json new file mode 100644 index 0000000..9d09ab5 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "projects": [ "src", "test" ], + "sdk": { + "version": "1.0.0-preview2-003131" + } +} diff --git a/pusher-dotnetcore-server.sln b/pusher-dotnetcore-server.sln new file mode 100644 index 0000000..a5599a0 --- /dev/null +++ b/pusher-dotnetcore-server.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6F931361-9183-46B1-90B2-12A0BB64364F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{31D1F264-8216-45B7-A4E1-2A545E32CAB5}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PusherServer", "src\PusherServer\PusherServer.xproj", "{6D75B971-15AA-4CBB-A160-886A524724AD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + 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}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D75B971-15AA-4CBB-A160-886A524724AD}.Release|Any CPU.Build.0 = 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/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/Project_Readme.html b/src/PusherServer/Project_Readme.html new file mode 100644 index 0000000..1a0f5b5 --- /dev/null +++ b/src/PusherServer/Project_Readme.html @@ -0,0 +1,187 @@ + + + + + Welcome to ASP.NET Core + + + + + + + + + + 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/src/PusherServer/PusherServer.xproj b/src/PusherServer/PusherServer.xproj new file mode 100644 index 0000000..cbf7bfb --- /dev/null +++ b/src/PusherServer/PusherServer.xproj @@ -0,0 +1,25 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 6d75b971-15aa-4cbb-a160-886a524724ad + PusherServer + .\obj + .\bin\ + v4.5.2 + + + + 2.0 + + + + + + + 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/src/PusherServer/project.json b/src/PusherServer/project.json new file mode 100644 index 0000000..a72ae08 --- /dev/null +++ b/src/PusherServer/project.json @@ -0,0 +1,48 @@ +{ + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.1", + "type": "platform" + }, + "Microsoft.AspNetCore.Diagnostics": "1.0.0", + + "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", + "Microsoft.Extensions.Logging.Console": "1.0.0" + }, + + "tools": { + "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dotnet5.6", + "portable-net45+win8" + ] + } + }, + + "buildOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } + }, + + "publishOptions": { + "include": [ + "wwwroot", + "web.config" + ] + }, + + "scripts": { + "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] + } +} 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 @@ + + + + + + + + + + + + From 23ae6596485f86fbd56392f2c7da33caf32886ea Mon Sep 17 00:00:00 2001 From: JasonH Date: Tue, 20 Dec 2016 14:15:17 -0600 Subject: [PATCH 3/9] Ported to .net core. --- src/PusherServer/AuthenticationData.cs | 78 +++ src/PusherServer/BatchEvent.cs | 28 + src/PusherServer/BatchTriggerBody.cs | 11 + src/PusherServer/ChannelsList.cs | 56 ++ src/PusherServer/CryptoHelper.cs | 34 + src/PusherServer/DefaultDeserializer.cs | 16 + src/PusherServer/DefaultSerializer.cs | 16 + src/PusherServer/Event.cs | 28 + src/PusherServer/EventIdData.cs | 28 + .../Exceptions/NotifyResponseException.cs | 32 + .../Exceptions/TriggerResponseException.cs | 29 + src/PusherServer/GetResult.cs | 46 ++ src/PusherServer/IAuthenticationData.cs | 24 + src/PusherServer/IDeserializeJsonStrings.cs | 15 + src/PusherServer/IGetResult.cs | 17 + src/PusherServer/INotifyResult.cs | 18 + src/PusherServer/IPusher.cs | 268 ++++++++ src/PusherServer/IPusherOptions.cs | 79 +++ src/PusherServer/IRequestResult.cs | 20 + src/PusherServer/ISerializeObjectsToJson.cs | 15 + src/PusherServer/ITriggerOptions.cs | 13 + src/PusherServer/ITriggerResult.cs | 15 + src/PusherServer/IWebHook.cs | 46 ++ src/PusherServer/Notification.cs | 11 + src/PusherServer/NotifyBody.cs | 50 ++ src/PusherServer/NotifyResult.cs | 44 ++ src/PusherServer/NotifySubscriberData.cs | 18 + src/PusherServer/PresenceChannelData.cs | 23 + src/PusherServer/Project_Readme.html | 187 ------ src/PusherServer/Pusher.cs | 618 ++++++++++++++++++ src/PusherServer/PusherOptions.cs | 252 +++++++ src/PusherServer/RawBodySerializer.cs | 32 + src/PusherServer/RequestResult.cs | 66 ++ src/PusherServer/TriggerBody.cs | 28 + src/PusherServer/TriggerOptions.cs | 14 + src/PusherServer/TriggerResult.cs | 44 ++ src/PusherServer/Util/ReadOnlyDictionary.cs | 367 +++++++++++ src/PusherServer/ValidationHelper.cs | 95 +++ src/PusherServer/WebHook.cs | 114 ++++ src/PusherServer/WebHookData.cs | 58 ++ src/PusherServer/WebHookEvent.cs | 14 + src/PusherServer/WebhookLevel.cs | 22 + src/PusherServer/project.json | 4 +- 43 files changed, 2804 insertions(+), 189 deletions(-) create mode 100644 src/PusherServer/AuthenticationData.cs create mode 100644 src/PusherServer/BatchEvent.cs create mode 100644 src/PusherServer/BatchTriggerBody.cs create mode 100644 src/PusherServer/ChannelsList.cs create mode 100644 src/PusherServer/CryptoHelper.cs create mode 100644 src/PusherServer/DefaultDeserializer.cs create mode 100644 src/PusherServer/DefaultSerializer.cs create mode 100644 src/PusherServer/Event.cs create mode 100644 src/PusherServer/EventIdData.cs create mode 100644 src/PusherServer/Exceptions/NotifyResponseException.cs create mode 100644 src/PusherServer/Exceptions/TriggerResponseException.cs create mode 100644 src/PusherServer/GetResult.cs create mode 100644 src/PusherServer/IAuthenticationData.cs create mode 100644 src/PusherServer/IDeserializeJsonStrings.cs create mode 100644 src/PusherServer/IGetResult.cs create mode 100644 src/PusherServer/INotifyResult.cs create mode 100644 src/PusherServer/IPusher.cs create mode 100644 src/PusherServer/IPusherOptions.cs create mode 100644 src/PusherServer/IRequestResult.cs create mode 100644 src/PusherServer/ISerializeObjectsToJson.cs create mode 100644 src/PusherServer/ITriggerOptions.cs create mode 100644 src/PusherServer/ITriggerResult.cs create mode 100644 src/PusherServer/IWebHook.cs create mode 100644 src/PusherServer/Notification.cs create mode 100644 src/PusherServer/NotifyBody.cs create mode 100644 src/PusherServer/NotifyResult.cs create mode 100644 src/PusherServer/NotifySubscriberData.cs create mode 100644 src/PusherServer/PresenceChannelData.cs delete mode 100644 src/PusherServer/Project_Readme.html create mode 100644 src/PusherServer/Pusher.cs create mode 100644 src/PusherServer/PusherOptions.cs create mode 100644 src/PusherServer/RawBodySerializer.cs create mode 100644 src/PusherServer/RequestResult.cs create mode 100644 src/PusherServer/TriggerBody.cs create mode 100644 src/PusherServer/TriggerOptions.cs create mode 100644 src/PusherServer/TriggerResult.cs create mode 100644 src/PusherServer/Util/ReadOnlyDictionary.cs create mode 100644 src/PusherServer/ValidationHelper.cs create mode 100644 src/PusherServer/WebHook.cs create mode 100644 src/PusherServer/WebHookData.cs create mode 100644 src/PusherServer/WebHookEvent.cs create mode 100644 src/PusherServer/WebhookLevel.cs diff --git a/src/PusherServer/AuthenticationData.cs b/src/PusherServer/AuthenticationData.cs new file mode 100644 index 0000000..fd0eb49 --- /dev/null +++ b/src/PusherServer/AuthenticationData.cs @@ -0,0 +1,78 @@ +using RestSharp.Serializers; +using System.Runtime.Serialization; + +namespace PusherServer +{ + [DataContract] + class AuthenticationData: IAuthenticationData + { + private string _appKey; + private string _appSecret; + private string _channelName; + private string _socketId; + private PresenceChannelData _presenceData; + + public AuthenticationData(string appKey, string appSecret, string channelName, string socketId) + { + ValidationHelper.ValidateChannelName(channelName); + ValidationHelper.ValidateSocketId(socketId); + + _appKey = appKey; + _appSecret = appSecret; + _channelName = channelName; + _socketId = socketId; + } + + public AuthenticationData(string appKey, string appSecret, string channelName, string socketId, PresenceChannelData presenceData): + this(appKey, appSecret, channelName, socketId) + { + _presenceData = presenceData; + } + + [DataMember(Name = "auth", IsRequired = true)] + public string auth + { + get + { + var serializer = new JsonSerializer(); + var stringToSign = _socketId + ":" + _channelName; + if (_presenceData != null) + { + var presenceJson = serializer.Serialize(_presenceData); + stringToSign += ":" + presenceJson; + } + + return _appKey + ":" + CryptoHelper.GetHmac256(_appSecret, stringToSign); + } + } + + /// + /// Double encoded JSON containing presence channel user information. + /// + [DataMember(Name = "channel_data", IsRequired = false, EmitDefaultValue = false)] + public string channel_data + { + get + { + string json = null; + if (_presenceData != null) + { + var serializer = new JsonSerializer(); + json = serializer.Serialize(_presenceData); + } + return json; + } + } + + public string ToJson() + { + var serializer = new JsonSerializer(); + return serializer.Serialize(this); + } + + public override string ToString() + { + return ToJson(); + } + } +} diff --git a/src/PusherServer/BatchEvent.cs b/src/PusherServer/BatchEvent.cs new file mode 100644 index 0000000..20c22d9 --- /dev/null +++ b/src/PusherServer/BatchEvent.cs @@ -0,0 +1,28 @@ +namespace PusherServer +{ + /// + /// Represents an event that is part of a batch trigger, for the purposes of serialisation + /// + internal class BatchEvent + { + /// + /// The name of the event + /// + public string name { get; set; } + + /// + /// The event data + /// + public string data { get; set; } + + /// + /// The channel the event should be triggered on. + /// + public string channel { get; set; } + + /// + /// The id of a socket to be excluded from receiving the event. + /// + public string socket_id { get; set; } + } +} diff --git a/src/PusherServer/BatchTriggerBody.cs b/src/PusherServer/BatchTriggerBody.cs new file mode 100644 index 0000000..1ff07ef --- /dev/null +++ b/src/PusherServer/BatchTriggerBody.cs @@ -0,0 +1,11 @@ + +namespace PusherServer +{ + /// + /// Represents the payload to be sent when triggering events + /// + class BatchTriggerBody + { + public BatchEvent[] batch { get; set; } + } +} diff --git a/src/PusherServer/ChannelsList.cs b/src/PusherServer/ChannelsList.cs new file mode 100644 index 0000000..e91b504 --- /dev/null +++ b/src/PusherServer/ChannelsList.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.Serialization; +using System.Collections.Generic; + +namespace PusherServer +{ + /* + {"channels":{"test_channel":{}}} + */ + + /// + /// A list of Channels received from the Pusher Server + /// + [DataContract] + public class ChannelsList + { + private Dictionary> _channelsInfo = null; + + /// + /// Gets or sets the Channel Info for a given Channel Name + /// + /// + /// A string representing the Channel Info + public Dictionary this[string channelName] + { + get + { + return _channelsInfo[channelName]; + } + set + { + _channelsInfo[channelName] = value; + } + } + + /// + /// Gets or sets all the Channel Info + /// + [DataMember(Name = "channels")] + public Dictionary> Channels + { + get + { + return _channelsInfo; + } + set + { + if (_channelsInfo != null) + { + throw new InvalidOperationException("Channels should only be set as part of deserialization."); + } + _channelsInfo = value; + } + } + } +} diff --git a/src/PusherServer/CryptoHelper.cs b/src/PusherServer/CryptoHelper.cs new file mode 100644 index 0000000..707c59a --- /dev/null +++ b/src/PusherServer/CryptoHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace PusherServer +{ + internal class CryptoHelper + { + + internal static string GetMd5Hash(string jsonData) + { + using (var md5 = MD5.Create()) + { + var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(jsonData)); + return BytesToHex(hash); + } + } + + private static string BytesToHex(IEnumerable byteArray) + { + return String.Concat(byteArray.Select(bytes => bytes.ToString("x2")).ToArray()); + } + + internal static string GetHmac256(string secret, string toSign) + { + var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); + var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(toSign)); + + return BytesToHex(hash); + } + } +} diff --git a/src/PusherServer/DefaultDeserializer.cs b/src/PusherServer/DefaultDeserializer.cs new file mode 100644 index 0000000..8214279 --- /dev/null +++ b/src/PusherServer/DefaultDeserializer.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace PusherServer +{ + /// + /// Default implmentation for deserializing an object + /// + public class DefaultDeserializer : IDeserializeJsonStrings + { + /// + public T Deserialize(string stringToDeserialize) + { + return JsonConvert.DeserializeObject(stringToDeserialize); + } + } +} \ No newline at end of file diff --git a/src/PusherServer/DefaultSerializer.cs b/src/PusherServer/DefaultSerializer.cs new file mode 100644 index 0000000..c090959 --- /dev/null +++ b/src/PusherServer/DefaultSerializer.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace PusherServer +{ + /// + /// Default implmentation for serializing an object + /// + public class DefaultSerializer : ISerializeObjectsToJson + { + /// + public string Serialize(object objectToSerialize) + { + return JsonConvert.SerializeObject(objectToSerialize); + } + } +} \ No newline at end of file diff --git a/src/PusherServer/Event.cs b/src/PusherServer/Event.cs new file mode 100644 index 0000000..e431025 --- /dev/null +++ b/src/PusherServer/Event.cs @@ -0,0 +1,28 @@ +namespace PusherServer +{ + /// + /// Represents an event for batch submission + /// + public class Event + { + /// + /// The event name + /// + public string EventName { get; set; } + + /// + /// The channel to which the event should be sent + /// + public string Channel { get; set; } + + /// + /// An optional socket ID which should not receive the event + /// + public string SocketId { get; set; } + + /// + /// The event data + /// + public object Data { get; set; } + } +} diff --git a/src/PusherServer/EventIdData.cs b/src/PusherServer/EventIdData.cs new file mode 100644 index 0000000..fbe7e85 --- /dev/null +++ b/src/PusherServer/EventIdData.cs @@ -0,0 +1,28 @@ + +using System.Collections.Generic; + +namespace PusherServer +{ + /// + /// Class used for handling the deserialisation of the Trigger HTTP response. + /// + public class EventIdData + { + private Dictionary _eventIds = new Dictionary(); + + /// + /// Dictionary of channel name to event ID for the triggered event. + /// + public Dictionary event_ids + { + get + { + return _eventIds; + } + set + { + _eventIds = value; + } + } + } +} \ No newline at end of file diff --git a/src/PusherServer/Exceptions/NotifyResponseException.cs b/src/PusherServer/Exceptions/NotifyResponseException.cs new file mode 100644 index 0000000..f7cabd4 --- /dev/null +++ b/src/PusherServer/Exceptions/NotifyResponseException.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer.Exceptions +{ + /// + /// Thrown when problems are detected with the response from the Pusher Notify HTTP endpoint. + /// + public class NotifyResponseException : Exception + { + /// + /// Create a new instance + /// + /// Description of the exception + public NotifyResponseException(string message) : + base(message) + { + } + + /// + /// Create a new instance + /// + /// Description of the exception + /// The inner exception + public NotifyResponseException(string message, Exception innerException) : + base(message, innerException) + { + } + } +} diff --git a/src/PusherServer/Exceptions/TriggerResponseException.cs b/src/PusherServer/Exceptions/TriggerResponseException.cs new file mode 100644 index 0000000..1a5929c --- /dev/null +++ b/src/PusherServer/Exceptions/TriggerResponseException.cs @@ -0,0 +1,29 @@ +using System; + +namespace PusherServer.Exceptions +{ + /// + /// Thrown when problems are detected with the response from the Pusher trigger HTTP endpoint. + /// + public class TriggerResponseException : Exception + { + /// + /// Create a new instance + /// + /// Description of the exception + public TriggerResponseException(string message) : + base(message) + { + } + + /// + /// Create a new instance + /// + /// Description of the exception + /// The inner exception + public TriggerResponseException(string message, Exception innerException) : + base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/PusherServer/GetResult.cs b/src/PusherServer/GetResult.cs new file mode 100644 index 0000000..df4e632 --- /dev/null +++ b/src/PusherServer/GetResult.cs @@ -0,0 +1,46 @@ +using System; +using System.Net; +using RestSharp; + +namespace PusherServer +{ + /// + /// Deserialised the result from a Rest Response + /// + /// The Type the Rest Response contains + public class GetResult : RequestResult, IGetResult + { + /// + /// Attempts to deserialise the data contained with a Rest Response + /// + /// The response containing the data to deserialise + /// + public GetResult(IRestResponse response, IDeserializeJsonStrings deserializer) : base(response) + { + if (deserializer == null) + { + throw new ArgumentNullException("deserializer", "An instance of a deserializer needs to be provided"); + } + + DeserializeResponse(response, deserializer); + } + + /// + /// Gets the data deserialised from the Rest Response + /// + public T Data { get; private set; } + + private void DeserializeResponse(IRestResponse response, IDeserializeJsonStrings deserializer) + { + try + { + Data = deserializer.Deserialize(response.Content); + } + catch (Exception e) + { + StatusCode = HttpStatusCode.BadRequest; + Body = string.Format("The HTTP response could not be deserialized to the expected type. The following exception occurred: {0}", e); + } + } + } +} diff --git a/src/PusherServer/IAuthenticationData.cs b/src/PusherServer/IAuthenticationData.cs new file mode 100644 index 0000000..0d13c3c --- /dev/null +++ b/src/PusherServer/IAuthenticationData.cs @@ -0,0 +1,24 @@ +namespace PusherServer +{ + /// + /// Interface for Authenticaton Data + /// + public interface IAuthenticationData + { + /// + /// Gets the Authetication String + /// + string auth { get; } + + /// + /// Double encoded JSON containing presence channel user information. + /// + string channel_data { get; } + + /// + /// Returns a Json representation of the authentication data. + /// + /// + string ToJson(); + } +} diff --git a/src/PusherServer/IDeserializeJsonStrings.cs b/src/PusherServer/IDeserializeJsonStrings.cs new file mode 100644 index 0000000..fd362ba --- /dev/null +++ b/src/PusherServer/IDeserializeJsonStrings.cs @@ -0,0 +1,15 @@ +namespace PusherServer +{ + /// + /// Contract that allows a JSON deserializer to be injected + /// + public interface IDeserializeJsonStrings + { + /// + /// Deserialize a JSON string into an object + /// + /// The JSON string to be deserialized into an object instance + /// A populated object + T Deserialize(string stringToDeserialize); + } +} \ No newline at end of file diff --git a/src/PusherServer/IGetResult.cs b/src/PusherServer/IGetResult.cs new file mode 100644 index 0000000..465bebd --- /dev/null +++ b/src/PusherServer/IGetResult.cs @@ -0,0 +1,17 @@ +namespace PusherServer +{ + /// + /// The result of a GET HTTP request to the Pusher REST API. + /// + /// The object type that the data returned from the request should be deserialized to. + public interface IGetResult: IRequestResult + { + /// + /// Gets the data returned from the request in a deserialized form. + /// + /// + /// The data. + /// + T Data { get; } + } +} diff --git a/src/PusherServer/INotifyResult.cs b/src/PusherServer/INotifyResult.cs new file mode 100644 index 0000000..08985f8 --- /dev/null +++ b/src/PusherServer/INotifyResult.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer +{ + /// + /// Interface for Notify Request Results + /// + public interface INotifyResult : IRequestResult + { + /// + /// Number of subscribers + /// + int NumberOfSubscribers { get; } + } +} diff --git a/src/PusherServer/IPusher.cs b/src/PusherServer/IPusher.cs new file mode 100644 index 0000000..8854c61 --- /dev/null +++ b/src/PusherServer/IPusher.cs @@ -0,0 +1,268 @@ +using System; + +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 interface IPusher + { + #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 + /// + /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON + /// + ITriggerResult Trigger(string channelName, string eventName, object 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 + /// + /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON + /// + ITriggerResult Trigger(string[] channelNames, string eventName, object data); + + /// + /// 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 + /// + /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON + /// + ITriggerResult Trigger(string channelName, string eventName, object data, ITriggerOptions options); + + /// + /// Triggers an event on the specified channels. + /// + /// The name 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. + /// Additional options to be used when triggering the event. See . + /// The result of the call to the REST API + /// + /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON + /// + ITriggerResult Trigger(string[] channelNames, string eventName, object data, ITriggerOptions options); + + /// + /// Triggers the events in the passed in array + /// + /// The events to trigger + /// The result of the call to the REST API + ITriggerResult Trigger(Event[] events); + + /// + /// Triggers an event on the specified channels in the background. + /// + /// The name of the channel to trigger the event on + /// The name of the event. + /// The data to be sent with the event. The event payload. + /// Method to call when the request has returned. + void TriggerAsync(string channelName, string eventName, object data, Action callback); + + /// + /// Triggers an event on the specified channels in the background. + /// + /// The name of the channel to trigger the event 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 . + /// Method to call when the request has returned. + void TriggerAsync(string channelName, string eventName, object data, ITriggerOptions options, Action callback); + + /// + /// Triggers an event on the specified channels in the background. + /// + /// The channels to trigger the event on + /// The name of the event. + /// The data to be sent with the event. The event payload. + /// Method to call when the request has returned. + void TriggerAsync(string[] channelNames, string eventName, object data, Action callback); + + /// + /// Triggers an event on the specified channels in the background. + /// + /// The channels to trigger the event 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 . + /// Method to call when the request has returned. + void TriggerAsync(string[] channelNames, string eventName, object data, ITriggerOptions options, Action callback); + + /// + /// Triggers the events in the passed in array asynchronously + /// + /// The events to trigger + /// Method to call when the request has returned. + /// The result of the call to the REST API + void TriggerAsync(Event[] events, Action callback); + + /// + /// Notification to native devices. + /// + /// array of interests that people should be subscribed to + /// The actual message to send in the APN + /// The title to send in the APN + /// The subtitle to send in the APN + /// These are used to specify an external URL to which Pusher will send information about the sent push notifications. This is particularly useful for debugging failed push notifications. + /// must be either "INFO" (errors-only) or "DEBUG" (everything). If omitted, it defaults to "INFO" + /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON + INotifyResult Notify(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level); + + /// + /// Notification to native devices, sincle interest + /// + /// Single interest we want to push to + /// The actual message to send in the APN + /// The title to send in the APN + /// The subtitle to send in the APN + /// These are used to specify an external URL to which Pusher will send information about the sent push notifications. This is particularly useful for debugging failed push notifications. + /// must be either "INFO" (errors-only) or "DEBUG" (everything). If omitted, it defaults to "INFO" + /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON + INotifyResult Notify(string interest, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level); + + /// + /// Calls the notify api in an asynchronously fashion + /// + /// Single interest we want to push to + /// The actual message to send in the APN + /// The title to send in the APN + /// The subtitle to send in the APN + /// These are used to specify an external URL to which Pusher will send information about the sent push notifications. This is particularly useful for debugging failed push notifications. + /// must be either "INFO" (errors-only) or "DEBUG" (everything). If omitted, it defaults to "INFO" + /// Method to call when the request has returned + void NotifyAsync(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level, Action callback); + + #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 + IAuthenticationData Authenticate(string channelName, string 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 + IAuthenticationData Authenticate(string channelName, string socketId, PresenceChannelData data); + + #endregion + + /// + /// Makes a GET request to the specified resource. Authentication is handled as part of the call. The data returned from the request is deserizlized to the object type defined by . + /// + /// + /// The resource. + /// The result of the GET request + IGetResult Get(string resource); + + /// + /// Makes a GET request to the specified resource. Authentication is handled as part of the call. The data returned from the request is deserizlized to the object type defined by . + /// + /// + /// The resource. + /// Additional parameters to be sent as part of the request query string. + /// + /// The result of the GET request + /// + IGetResult Get(string resource, object parameters); + + /// + /// Handle an incoming WebHook and validate it. + /// + /// The signature of the incoming WebHook + /// The body of the incoming Webhook request + /// A WebHook helper. + IWebHook ProcessWebHook(string signature, string body); + + /// + /// Queries the Pusher API for the Users of a Presence Channel + /// + /// The type of object that will be returned by the API + /// The name of the channel to query + /// The result of the Presence Channel Users query + IGetResult FetchUsersFromPresenceChannel(string channelName); + + /// + /// Queries the Pusher API for the Users of a Presence Channel asynchronously + /// + /// The type of object that will be returned by the API + /// The name of the channel to query + /// The callback to receive the result of the query + void FetchUsersFromPresenceChannelAsync(string channelName, Action> callback); + + /// + /// Queries the Pusher API for the state of a Channel + /// + /// The type of object that will be returned by the API + /// The name of the channel to query + /// An object containing a list of attributes to include in the query + /// The result of the Channel State query + IGetResult FetchStateForChannel(string channelName, object info); + + /// + /// Queries the Pusher API for the state of a Channel asynchronously + /// + /// The type of object that will be returned by the API + /// The name of the channel to query + /// The callback to receive the result of the query + void FetchStateForChannelAsync(string channelName, Action> callback); + + /// + /// Queries the Pusher API for the state of a Channel asynchronously + /// + /// The type of object that will be returned by the API + /// The name of the channel to query + /// An object containing a list of attributes to include in the query + /// The callback to receive the result of the query + void FetchStateForChannelAsync(string channelName, object info, Action> callback); + + /// + /// Queries the Pusher API for the state of all channels based upon the info object + /// + /// The type of object that will be returned by the API + /// An object containing a list of attributes to include in the query + /// The result of the Channels State query + IGetResult FetchStateForChannels(object info); + + /// + /// Queries the Pusher API for the state of all channels based upon the info object + /// + /// The type of object that will be returned by the API + /// The callback to receive the result of the query + void FetchStateForChannelsAsync(Action> callback); + + /// + /// Queries the Pusher API for the state of all channels based upon the info object + /// + /// The type of object that will be returned by the API + /// An object containing a list of attributes to include in the query + /// The callback to receive the result of the query + void FetchStateForChannelsAsync(object info, Action> callback); + } +} \ No newline at end of file diff --git a/src/PusherServer/IPusherOptions.cs b/src/PusherServer/IPusherOptions.cs new file mode 100644 index 0000000..baab563 --- /dev/null +++ b/src/PusherServer/IPusherOptions.cs @@ -0,0 +1,79 @@ +using System; +using RestSharp; + +namespace PusherServer +{ + /// + /// Interface for Pusher Options + /// + public interface IPusherOptions + { + /// + /// Gets or sets a value indicating whether calls to the Pusher REST API are over HTTP or HTTPS. + /// + /// + /// true if encrypted; otherwise, false. + /// + bool Encrypted { get; set; } + + /// + /// Gets or sets the REST API port that the HTTP calls will be made to. + /// + /// + /// The port. + /// + int Port { get; set; } + + /// + /// Gets or sets the Json Serializer + /// + ISerializeObjectsToJson JsonSerializer { get; set; } + + /// + /// Gets or sets the Json Deserializer + /// + IDeserializeJsonStrings JsonDeserializer { get; set; } + + /// + /// Gets or sets the rest client. Generally only expected to be used for testing. + /// + /// + /// The rest client. + /// + IRestClient RestClient { get; set; } + + /// + /// The host of the HTTP API endpoint excluding the scheme e.g. api.pusherapp.com + /// + /// If a scheme is found at the start of the host value + string HostName { get; set; } + + /// + /// The host of the HTTP Api endoind for push notifications e.g. nativepush-cluster1.pusher.com + /// + string HostName_Notificaiton { get; set; } + + /// + /// The vrsion of the the push notificaiton service e.g. v1 + /// + string Notificaiton_Version { get; set; } + + /// + /// The prefix to be used to form the notificaiton service url e.g. server_api + /// + string Notification_Prefix { get; set; } + + /// + /// The cluster where the application was created, e.g. eu + /// + string Cluster { get; set; } + + /// + /// Gets the base Url based on the set Options + /// + /// The constructed URL + Uri GetBaseUrl(); + + Uri GetBaseNotificationUrl(); + } +} diff --git a/src/PusherServer/IRequestResult.cs b/src/PusherServer/IRequestResult.cs new file mode 100644 index 0000000..ec19b89 --- /dev/null +++ b/src/PusherServer/IRequestResult.cs @@ -0,0 +1,20 @@ +using System.Net; + +namespace PusherServer +{ + /// + /// Base interface for all Request Results + /// + public interface IRequestResult + { + /// + /// Gets the Body from a Request Result + /// + string Body { get; } + + /// + /// Gets the Status Code from a Request Result + /// + HttpStatusCode StatusCode { get; } + } +} diff --git a/src/PusherServer/ISerializeObjectsToJson.cs b/src/PusherServer/ISerializeObjectsToJson.cs new file mode 100644 index 0000000..b2200c6 --- /dev/null +++ b/src/PusherServer/ISerializeObjectsToJson.cs @@ -0,0 +1,15 @@ +namespace PusherServer +{ + /// + /// Contract that allows a JSON serializer to be injected + /// + public interface ISerializeObjectsToJson + { + /// + /// Serialize an object + /// + /// The object to be serialized into a JSON string + /// The passed in object as a JSON string + string Serialize(object objectToSerialize); + } +} diff --git a/src/PusherServer/ITriggerOptions.cs b/src/PusherServer/ITriggerOptions.cs new file mode 100644 index 0000000..7cdeeff --- /dev/null +++ b/src/PusherServer/ITriggerOptions.cs @@ -0,0 +1,13 @@ +namespace PusherServer +{ + /// + /// Additional options that can be used when triggering an event. + /// + public interface ITriggerOptions + { + /// + /// Gets or sets the Socket ID for a consuming Trigger + /// + string SocketId { get; set; } + } +} diff --git a/src/PusherServer/ITriggerResult.cs b/src/PusherServer/ITriggerResult.cs new file mode 100644 index 0000000..458b9de --- /dev/null +++ b/src/PusherServer/ITriggerResult.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace PusherServer +{ + /// + /// Interface for Trigger Request Results + /// + public interface ITriggerResult: IRequestResult + { + /// + /// Gets the Event IDs related to this Trigger Event + /// + IDictionary EventIds { get; } + } +} diff --git a/src/PusherServer/IWebHook.cs b/src/PusherServer/IWebHook.cs new file mode 100644 index 0000000..e22a6bf --- /dev/null +++ b/src/PusherServer/IWebHook.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace PusherServer +{ + /// + /// Interface for Web Hooks + /// + public interface IWebHook + { + /// + /// Indicates if the WebHook has validated. + /// + bool IsValid + { + get; + } + + /// + /// The Events in the WebHook + /// + Dictionary[] Events + { + get; + } + + + /// + /// The timestamp of the WebHook + /// + DateTime Time + { + get; + } + + /// + /// An array of validation errors. If is true then the array + /// will have no elements. + /// + string[] ValidationErrors + { + get; + } + + } +} \ No newline at end of file diff --git a/src/PusherServer/Notification.cs b/src/PusherServer/Notification.cs new file mode 100644 index 0000000..0233f1e --- /dev/null +++ b/src/PusherServer/Notification.cs @@ -0,0 +1,11 @@ +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; } + } +} diff --git a/src/PusherServer/NotifyBody.cs b/src/PusherServer/NotifyBody.cs new file mode 100644 index 0000000..b38f4ef --- /dev/null +++ b/src/PusherServer/NotifyBody.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer +{ + public class NotifyBody + { + public string[] interests { get; set; } + public Apns apns { get; set; } + public string webhook_url { get; set; } + public string webhook_level { get; set; } + + public NotifyBody() + { + apns = new Apns(); + } + } + + public class Apns + { + public Aps aps { get; set; } + + public Apns() + { + aps = new Aps(); + } + } + + public class Aps + { + public Alert alert { get; set; } + public Notification notification { get; set; } + + public Aps() + { + alert = new Alert(); + notification = new Notification(); + } + } + + public class Alert + { + public string body { get; set; } + public string title { get; set; } + public string subtitle { get; set; } + } + +} diff --git a/src/PusherServer/NotifyResult.cs b/src/PusherServer/NotifyResult.cs new file mode 100644 index 0000000..f8e3660 --- /dev/null +++ b/src/PusherServer/NotifyResult.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using PusherServer.Exceptions; +using RestSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer +{ + internal class NotifyResult : RequestResult, INotifyResult + { + private readonly int _numberOfSubscribers; + + /// + /// Constructs a new instance of a NotifyResult based upon a passed in Rest Response + /// + /// The Rest Response which will form the basis of this Notify Result + public NotifyResult(IRestResponse response) : base(response) + { + NotifySubscriberData subscriberData = null; + try + { + subscriberData = JsonConvert.DeserializeObject(response.Content); + } + catch (Exception) + { + string msg = + string.Format("The response body from the Pusher HTTP endpoint could not be parsed as JSON: {0}{1}", + Environment.NewLine, + response.Content); + throw new NotifyResponseException(msg); + } + + _numberOfSubscribers = subscriberData.number_of_subscribers; + } + + /// + public int NumberOfSubscribers + { + get { return this._numberOfSubscribers; } + } + } +} diff --git a/src/PusherServer/NotifySubscriberData.cs b/src/PusherServer/NotifySubscriberData.cs new file mode 100644 index 0000000..a05cce0 --- /dev/null +++ b/src/PusherServer/NotifySubscriberData.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer +{ + /// + /// Response payload from notify call + /// + public class NotifySubscriberData + { + /// + /// Number of subscribers to the listed events + /// + public int number_of_subscribers { get; set; } + } +} diff --git a/src/PusherServer/PresenceChannelData.cs b/src/PusherServer/PresenceChannelData.cs new file mode 100644 index 0000000..ba27ed6 --- /dev/null +++ b/src/PusherServer/PresenceChannelData.cs @@ -0,0 +1,23 @@ + +namespace PusherServer +{ + /// + /// Information about a user who is subscribing to a presence channel. + /// + public class PresenceChannelData + { + /// + /// A unique user identifier for the user witin the application. + /// + /// + /// Pusher uses this to uniquely identify a user. So, if multiple users are given the same user_id + /// the second of these users will be ignored and won't be represented on the presence channel. + /// + public string user_id { get; set; } + + /// + /// Arbitrary additional information about the user. + /// + public object user_info { get; set; } + } +} diff --git a/src/PusherServer/Project_Readme.html b/src/PusherServer/Project_Readme.html deleted file mode 100644 index 1a0f5b5..0000000 --- a/src/PusherServer/Project_Readme.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - Welcome to ASP.NET Core - - - - - - - - - - diff --git a/src/PusherServer/Pusher.cs b/src/PusherServer/Pusher.cs new file mode 100644 index 0000000..fa17c39 --- /dev/null +++ b/src/PusherServer/Pusher.cs @@ -0,0 +1,618 @@ +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.GetBaseUrl(); + _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.GetBaseUrl(); + _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/src/PusherServer/PusherOptions.cs b/src/PusherServer/PusherOptions.cs new file mode 100644 index 0000000..7fe3242 --- /dev/null +++ b/src/PusherServer/PusherOptions.cs @@ -0,0 +1,252 @@ +using System; +using System.Text.RegularExpressions; +using RestSharp; + +namespace PusherServer +{ + /// + /// Options to be set on the Pusher instance. + /// + public class PusherOptions : IPusherOptions + { + /// + /// The default Rest API Host for contacting the Pusher server, it does not contain a cluster name + /// + public const string DEFAULT_REST_API_HOST = "api.pusherapp.com"; + /// + /// The default Notificaiton API Host for contacting the Noitificaiton server + /// + public const string DEFAULT_NOTIFICATION_REST_API_HOST = "nativepush-cluster1.pusher.com"; + + /// + /// The default version for the Notificaiton service + /// + public const string DEFAULT_NOTIFICATION_VERSION = "v1"; + + /// + /// The default prefix for the Notificaiton Service + /// + public const string DEFAULT_NOTIFICATION_PREFIX = "server_api"; + + private static int DEFAULT_HTTPS_PORT = 443; + private static int DEFAULT_HTTP_PORT = 80; + + IRestClient _client; + bool _encrypted = false; + bool _portModified = false; + bool _hostSet = false; + int _port = DEFAULT_HTTP_PORT; + string _hostName = null; + string _notificationHostName = null; + string _cluster = null; + string _notificationVersion = null; + string _notificationPrefix = null; + ISerializeObjectsToJson _jsonSerializer; + IDeserializeJsonStrings _jsonDeserializer; + + /// + public bool Encrypted + { + get + { + return _encrypted; + } + set + { + _encrypted = value; + if (_encrypted && _portModified == false) + { + _port = DEFAULT_HTTPS_PORT; + } + } + } + + /// + public int Port + { + get + { + return _port; + } + set + { + _port = value; + _portModified = true; + } + } + + /// + /// Set the cluster only if there is no custom host defined. + /// + public string Cluster + { + get + { + return _cluster; + } + set + { + if (_hostSet == false) { + _cluster = value; + _hostName = "api-"+_cluster+".pusher.com"; + } + } + } + + /// + public IRestClient RestClient + { + get + { + if (_client == null) + { + _client = new RestClient(GetBaseUrl()); + } + + return _client; + } + set { _client = value; } + } + + /// + /// Gets or sets the HostName to use in the base URL + /// + public string HostName + { + get + { + return _hostName ?? DEFAULT_REST_API_HOST; + } + set + { + if (Regex.IsMatch(value, "^.*://")) + { + string msg = string.Format("The scheme should not be present in the host value: {0}", value); + throw new FormatException(msg); + } + + _hostSet = true; + _cluster = null; + _hostName = value; + } + } + + /// + /// Gets or sets the HostName to use in the notificaiton base URL + /// + public string HostName_Notificaiton + { + get + { + return _notificationHostName ?? DEFAULT_NOTIFICATION_REST_API_HOST; + } + set + { + if (Regex.IsMatch(value, "^.*://")) + { + string msg = string.Format("The scheme should not be present in the host value: {0}", value); + throw new FormatException(msg); + } + + _hostSet = true; + _cluster = null; + _notificationHostName = value; + } + } + /// + /// The vrsion of the the push notificaiton service e.g. v1 + /// + public string Notificaiton_Version + { + get + { + return _notificationVersion ?? DEFAULT_NOTIFICATION_VERSION; + } + set + { + _notificationVersion = value; + } + } + + /// + /// The prefix to be used to form the notificaiton service url e.g. server_api + /// + public string Notification_Prefix + { + get + { + return _notificationPrefix ?? DEFAULT_NOTIFICATION_PREFIX; + } + set + { + + _notificationPrefix = value; + } + } + + /// + /// Gets or sets the Json Serializer + /// + public ISerializeObjectsToJson JsonSerializer + { + get + { + if (_jsonSerializer == null) + { + _jsonSerializer = new DefaultSerializer(); + } + + return _jsonSerializer; + + } + set { _jsonSerializer = value; } + } + + /// + /// Gets or sets the Json Deserializer + /// + public IDeserializeJsonStrings JsonDeserializer + { + get + { + if (_jsonDeserializer == null) + { + _jsonDeserializer = new DefaultDeserializer(); + } + + return _jsonDeserializer; + } + set + { + _jsonDeserializer = value; + } + } + + /// + public Uri GetBaseUrl() + { + string baseUrl = (Encrypted ? "https" : "http") + "://" + HostName;// + GetPort(); + + 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; + + if (Port != DEFAULT_HTTP_PORT) + { + port += (":" + Port); + } + + return port; + } + } +} diff --git a/src/PusherServer/RawBodySerializer.cs b/src/PusherServer/RawBodySerializer.cs new file mode 100644 index 0000000..29b0d3d --- /dev/null +++ b/src/PusherServer/RawBodySerializer.cs @@ -0,0 +1,32 @@ +using System; + +namespace PusherServer +{ + /// + /// An implementation of the that passes through the raw string. + /// + public class RawBodySerializer : ISerializeObjectsToJson + { + /// + /// Presumes we are getting a string as the body, and passes it through. + /// + /// The string body to pass through. + /// The body passed in. + /// Thrown if the body provided is not a string. + public string Serialize(object body) + { + if (body == null) + { + return string.Empty; + } + + var bodyAsString = body as string; + if(bodyAsString == null) + { + throw new ArgumentException("The RawBodySerializer only supports strings for messages. The body type was: " + body.GetType().Name, "body"); + } + + return bodyAsString; + } + } +} \ No newline at end of file diff --git a/src/PusherServer/RequestResult.cs b/src/PusherServer/RequestResult.cs new file mode 100644 index 0000000..d70fa05 --- /dev/null +++ b/src/PusherServer/RequestResult.cs @@ -0,0 +1,66 @@ +using System; +using System.Net; +using RestSharp; + +namespace PusherServer +{ + /// + /// Abstract base class for results coming back from request to the Pusher servers + /// + public abstract class RequestResult : IRequestResult + { + string _body = null; + private HttpStatusCode _statusCode; + + /// + /// Constructor to constract the abstract base class for classes derived from RequestResults + /// + /// + public RequestResult(IRestResponse response) + { + if (response == null) + { + throw new ArgumentNullException("response"); + } + + _body = response.Content; + _statusCode = response.StatusCode; + + Response = response; + } + + /// + /// Gets the Status Code returned in the wrapped Rest Response + /// + public HttpStatusCode StatusCode + { + get { return _statusCode; } + protected set { _statusCode = value; } + } + + /// + /// Gets the Body returned in the wrapped Rest Response + /// + public string Body + { + get { return _body; } + protected set { _body = value; } + } + + /// + /// Gets the original content that was returned in the response, if the response returned was Bad + /// + public string OriginalContent + { + get + { + return Response != null ? Response.Content : string.Empty; + } + } + + /// + /// Gets the original response from the rest service + /// + public IRestResponse Response { get; private set; } + } +} diff --git a/src/PusherServer/TriggerBody.cs b/src/PusherServer/TriggerBody.cs new file mode 100644 index 0000000..d858144 --- /dev/null +++ b/src/PusherServer/TriggerBody.cs @@ -0,0 +1,28 @@ +namespace PusherServer +{ + /// + /// Represents the payload to be sent when triggering events + /// + internal class TriggerBody + { + /// + /// The name of the event + /// + public string name { get; set; } + + /// + /// The event data + /// + public string data { get; set; } + + /// + /// The channels the event should be triggered on. + /// + public string[] channels { get; set; } + + /// + /// The id of a socket to be excluded from receiving the event. + /// + public string socket_id { get; set; } + } +} diff --git a/src/PusherServer/TriggerOptions.cs b/src/PusherServer/TriggerOptions.cs new file mode 100644 index 0000000..a222973 --- /dev/null +++ b/src/PusherServer/TriggerOptions.cs @@ -0,0 +1,14 @@ + +namespace PusherServer +{ + /// + /// Represents the Options that can be used by A Trigger + /// + public class TriggerOptions: ITriggerOptions + { + /// + /// Gets or sets the Socket ID for the consuming Trigger + /// + public string SocketId { get; set; } + } +} diff --git a/src/PusherServer/TriggerResult.cs b/src/PusherServer/TriggerResult.cs new file mode 100644 index 0000000..c8d19ba --- /dev/null +++ b/src/PusherServer/TriggerResult.cs @@ -0,0 +1,44 @@ +using System; +using System.Net; +using System.Collections.Generic; +using PusherServer.Exceptions; +using PusherServer.Util; +using RestSharp; +using Newtonsoft.Json; + +namespace PusherServer +{ + internal class TriggerResult : RequestResult, ITriggerResult + { + private readonly IDictionary _eventIds; + + /// + /// Constructs a new instance of a TriggerResult based upon a passed in Rest Response + /// + /// The Rest Response which will form the basis of this Trigger Result + public TriggerResult(IRestResponse response) : base(response) + { + EventIdData eventIdData = null; + try + { + eventIdData = JsonConvert.DeserializeObject(response.Content); + } + catch (Exception) + { + string msg = + string.Format("The response body from the Pusher HTTP endpoint could not be parsed as JSON: {0}{1}", + Environment.NewLine, + response.Content); + throw new TriggerResponseException(msg); + } + + _eventIds = new ReadOnlyDictionary(eventIdData.event_ids); + } + + /// + public IDictionary EventIds + { + get { return this._eventIds; } + } + } +} \ No newline at end of file diff --git a/src/PusherServer/Util/ReadOnlyDictionary.cs b/src/PusherServer/Util/ReadOnlyDictionary.cs new file mode 100644 index 0000000..c51cd9d --- /dev/null +++ b/src/PusherServer/Util/ReadOnlyDictionary.cs @@ -0,0 +1,367 @@ +namespace PusherServer.Util +{ + // From: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=29 + + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Threading; + + /// + /// Provides the base class for a generic read-only dictionary. + /// + /// + /// The type of keys in the dictionary. + /// + /// + /// The type of values in the dictionary. + /// + /// + /// + /// An instance of the ReadOnlyDictionary generic class is + /// always read-only. A dictionary that is read-only is simply a + /// dictionary with a wrapper that prevents modifying the + /// dictionary; therefore, if changes are made to the underlying + /// dictionary, the read-only dictionary reflects those changes. + /// See for a modifiable version of + /// this class. + /// + /// + /// Notes to Implementers This base class is provided to + /// make it easier for implementers to create a generic read-only + /// custom dictionary. Implementers are encouraged to extend this + /// base class instead of creating their own. + /// + /// + [Serializable] + [DebuggerDisplay("Count = {Count}")] + [ComVisible(false)] + [DebuggerTypeProxy(typeof(ReadOnlyDictionaryDebugView<,>))] + public class ReadOnlyDictionary : IDictionary, + ICollection + { + private readonly IDictionary source; + private object syncRoot; + + /// + /// Initializes a new instance of the + /// class that wraps + /// the supplied . + /// + /// The + /// that will be wrapped. + /// + /// Thrown when the dictionary is null. + /// + public ReadOnlyDictionary(IDictionary dictionaryToWrap) + { + if (dictionaryToWrap == null) + { + throw new ArgumentNullException("dictionaryToWrap"); + } + + this.source = dictionaryToWrap; + } + + /// + /// Gets the number of key/value pairs contained in the + /// . + /// + /// The number of key/value pairs. + /// The number of key/value pairs contained in the + /// . + public int Count + { + get { return this.source.Count; } + } + + /// Gets a collection containing the keys in the + /// . + /// A + /// containing the keys. + /// A + /// + /// containing the keys in the + /// . + /// + public ICollection Keys + { + get { return this.source.Keys; } + } + + /// + /// Gets a collection containing the values of the + /// . + /// + /// The collection of values. + public ICollection Values + { + get { return this.source.Values; } + } + + /// Gets a value indicating whether the dictionary is read-only. + /// This value will always be true. + bool ICollection>.IsReadOnly + { + get { return true; } + } + + /// + /// Gets a value indicating whether access to the dictionary + /// is synchronized (thread safe). + /// + bool ICollection.IsSynchronized + { + get { return false; } + } + + /// + /// Gets an object that can be used to synchronize access to dictionary. + /// + object ICollection.SyncRoot + { + get + { + if (this.syncRoot == null) + { + ICollection collection = this.source as ICollection; + + if (collection != null) + { + this.syncRoot = collection.SyncRoot; + } + else + { + Interlocked.CompareExchange(ref this.syncRoot, new object(), null); + } + } + + return this.syncRoot; + } + } + + /// + /// Gets or sets the value associated with the specified key. + /// + /// + /// The value associated with the specified key. If the specified key + /// is not found, a get operation throws a + /// , + /// and a set operation creates a new element with the specified key. + /// + /// The key of the value to get or set. + /// + /// Thrown when the key is null. + /// + /// + /// The property is retrieved and key does not exist in the collection. + /// + public TValue this[TKey key] + { + get { return this.source[key]; } + set { ThrowNotSupportedException(); } + } + + /// This method is not supported by the + /// . + /// + /// The object to use as the key of the element to add. + /// + /// The object to use as the value of the element to add. + void IDictionary.Add(TKey key, TValue value) + { + ThrowNotSupportedException(); + } + + /// Determines whether the + /// contains the specified key. + /// + /// True if the contains + /// an element with the specified key; otherwise, false. + /// + /// The key to locate in the + /// . + /// + /// Thrown when the key is null. + /// + public bool ContainsKey(TKey key) + { + return this.source.ContainsKey(key); + } + + /// + /// This method is not supported by the . + /// + /// The key of the element to remove. + /// + /// True if the element is successfully removed; otherwise, false. + /// + bool IDictionary.Remove(TKey key) + { + ThrowNotSupportedException(); + return false; + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// When this method returns, contains the value + /// associated with the specified key, if the key is found; + /// otherwise, the default value for the type of the value parameter. + /// This parameter is passed uninitialized. + /// + /// true if the contains + /// an element with the specified key; otherwise, false. + /// + public bool TryGetValue(TKey key, out TValue value) + { + return this.source.TryGetValue(key, out value); + } + + /// This method is not supported by the + /// . + /// + /// The object to add to the . + /// + void ICollection>.Add( + KeyValuePair item) + { + ThrowNotSupportedException(); + } + + /// This method is not supported by the + /// . + void ICollection>.Clear() + { + ThrowNotSupportedException(); + } + + /// + /// Determines whether the contains a + /// specific value. + /// + /// + /// The object to locate in the . + /// + /// + /// true if item is found in the ICollection; + /// otherwise, false. + /// + bool ICollection>.Contains( + KeyValuePair item) + { + ICollection> collection = this.source; + + return collection.Contains(item); + } + + /// + /// Copies the elements of the ICollection to an Array, starting at a + /// particular Array index. + /// + /// The one-dimensional Array that is the + /// destination of the elements copied from ICollection. + /// The Array must have zero-based indexing. + /// + /// + /// The zero-based index in array at which copying begins. + /// + void ICollection>.CopyTo( + KeyValuePair[] array, int arrayIndex) + { + ICollection> collection = this.source; + collection.CopyTo(array, arrayIndex); + } + + /// This method is not supported by the + /// . + /// + /// The object to remove from the ICollection. + /// + /// Will never return a value. + bool ICollection>.Remove(KeyValuePair item) + { + ThrowNotSupportedException(); + return false; + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A IEnumerator that can be used to iterate through the collection. + /// + IEnumerator> + IEnumerable>.GetEnumerator() + { + IEnumerable> enumerator = this.source; + + return enumerator.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An IEnumerator that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.source.GetEnumerator(); + } + + /// + /// For a description of this member, see . + /// + /// + /// The one-dimensional Array that is the destination of the elements copied from + /// ICollection. The Array must have zero-based indexing. + /// + /// + /// The zero-based index in Array at which copying begins. + /// + void ICollection.CopyTo(Array array, int index) + { + ICollection collection = + new List>(this.source); + + collection.CopyTo(array, index); + } + + private static void ThrowNotSupportedException() + { + throw new NotSupportedException("This Dictionary is read-only"); + } + } + + internal sealed class ReadOnlyDictionaryDebugView + { + private IDictionary dict; + + public ReadOnlyDictionaryDebugView( + ReadOnlyDictionary dictionary) + { + if (dictionary == null) + { + throw new ArgumentNullException("dictionary"); + } + + this.dict = dictionary; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair[] Items + { + get + { + KeyValuePair[] array = + new KeyValuePair[this.dict.Count]; + this.dict.CopyTo(array, 0); + return array; + } + } + } +} \ No newline at end of file diff --git a/src/PusherServer/ValidationHelper.cs b/src/PusherServer/ValidationHelper.cs new file mode 100644 index 0000000..aa87dc5 --- /dev/null +++ b/src/PusherServer/ValidationHelper.cs @@ -0,0 +1,95 @@ +using System; +using System.Text.RegularExpressions; + +namespace PusherServer +{ + /// + /// Helps validation of channel names and socket_id values. + /// + public static class ValidationHelper + { + /// + /// A regular expression to check that a channel name is in a format allowed and accepted by Pusher. + /// + public static Regex CHANNEL_NAME_REGEX = new Regex(@"\A[a-zA-Z0-9_=@,.;\-]+\z", RegexOptions.Singleline); + + /// + /// The maximum length of a channel name allowed by Pusher. + /// + public static int CHANNEL_NAME_MAX_LENGTH = 164; + + /// + /// A regular expression to check that a socket_id is in a format allowed and accepted by Pusher. + /// + public static Regex SOCKET_ID_REGEX = new Regex(@"\A\d+\.\d+\z", RegexOptions.Singleline); + + /// + /// The maximum event batch size accepted by Pusher + /// + public static int MAX_BATCH_SIZE = 100; + + /// + /// Validate a socket_id value + /// + /// The value to be checked. + /// If the socket_id name is not in the allowed format. + internal static void ValidateSocketId(string socketId) + { + if (socketId != null && SOCKET_ID_REGEX.IsMatch(socketId) == false) + { + string msg = + string.Format("socket_id \"{0}\" was not in the form: {1}", socketId, SOCKET_ID_REGEX.ToString()); + throw new FormatException(msg); + } + } + + /// + /// Validate a single channel name is in the allowed format. + /// + /// The channel name to be checked + /// If the channel name is not in the allowed format. + internal static void ValidateChannelName(string channelName) + { + if(channelName.Length > CHANNEL_NAME_MAX_LENGTH) + { + string msg = + string.Format("The length of the channel name was greater than the allowed {0} characters", CHANNEL_NAME_MAX_LENGTH); + throw new ArgumentOutOfRangeException(msg); + } + + if (CHANNEL_NAME_REGEX.IsMatch(channelName) == false) + { + string msg = + string.Format("channel name \"{0}\" was not in the form: {1}", channelName, CHANNEL_NAME_REGEX.ToString()); + throw new FormatException(msg); + } + } + + /// + /// Validate an array of channel names + /// + /// The array of channel names + /// If any channel names are not in the allowed format. + internal static void ValidateChannelNames(string[] channelNames) + { + foreach(string name in channelNames) + { + ValidateChannelName(name); + } + } + + internal static void ValidateBatchEvents(Event[] events) + { + if (events.Length > 100) + { + throw new ArgumentOutOfRangeException(string.Format("Only {0} events permitted per batch, {1} submitted", MAX_BATCH_SIZE, events.Length)); + } + + foreach (Event e in events) + { + ValidateChannelName(e.Channel); + ValidateSocketId(e.SocketId); + } + } + } +} diff --git a/src/PusherServer/WebHook.cs b/src/PusherServer/WebHook.cs new file mode 100644 index 0000000..bda98a9 --- /dev/null +++ b/src/PusherServer/WebHook.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PusherServer +{ + internal class WebHook: IWebHook + { + private readonly WebHookData _webHookData; + private readonly List _validationErrors; + private IDeserializeJsonStrings _jsonDeserializer; + + internal WebHook(string secret, string signature, string body) + { + if(string.IsNullOrEmpty(secret)) + { + throw new ArgumentException("A secret must be provided", "secret"); + } + + this._validationErrors = new List(); + + this._webHookData = ValidateWebHook(secret, signature, body); + } + + private WebHookData ValidateWebHook(string secret, string signature, string body) + { + WebHookData parsedWebHookData = null; + + var signatureNullOrEmpth = string.IsNullOrEmpty(signature); + if(signatureNullOrEmpth == true) { + this._validationErrors.Add("The supplied signature to check was null or empty. A signature to check must be provided."); + } + + if (string.IsNullOrEmpty(body) == true) + { + this._validationErrors.Add("The supplied body to check was null or empty. A body to check must be provided."); + } + else + { + try + { + parsedWebHookData = JsonDeserializer.Deserialize(body); + } + catch (Exception e) + { + var validationError = string.Format("Exception occurred parsing the body as JSON: {0}{1}", Environment.NewLine, e.StackTrace); + this._validationErrors.Add(validationError); + } + } + + if (parsedWebHookData != null) + { + var expectedSignature = CryptoHelper.GetHmac256(secret, body); + var signatureIsValid = (signature == expectedSignature); + if (signatureIsValid == false) + { + this._validationErrors.Add( + string.Format("The signature did not validate. Expected {0}. Got {1}", signature, expectedSignature) + ); + } + } + return parsedWebHookData; + } + + public bool IsValid + { + get + { + return (this.ValidationErrors.Length == 0); + } + } + + public Dictionary[] Events + { + get + { + return this._webHookData.events; + } + } + + public DateTime Time + { + get + { + return this._webHookData.Time; + } + } + + public string[] ValidationErrors + { + get + { + return this._validationErrors.ToArray(); + } + } + + /// + /// Gets or sets the JSON Deserializer to use + /// + public IDeserializeJsonStrings JsonDeserializer + { + get + { + if (_jsonDeserializer == null) + { + _jsonDeserializer = new DefaultDeserializer(); + } + + return _jsonDeserializer; + } + set { _jsonDeserializer = value; } + } + } +} diff --git a/src/PusherServer/WebHookData.cs b/src/PusherServer/WebHookData.cs new file mode 100644 index 0000000..9cf1ae9 --- /dev/null +++ b/src/PusherServer/WebHookData.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +namespace PusherServer +{ + /// + /// Represents the Data payload of a Web Hook + /// + public class WebHookData + { + private DateTime _time; + + /// + /// Gets or sets the Time the Web Hook was created in Milliseconds + /// + public string time_ms + { + get + { + // This should not be used. + return GetUnixTimestampMillis(this.Time).ToString(); + } + set + { + long unixTimeStamp = long.Parse(value); + _time = DateTimeFromUnixTimestampMillis(unixTimeStamp); + } + } + /// + /// Gets or sets the Events being triggered + /// + public Dictionary[] events { get; set; } + + /// + /// Gets the Time the Web Hook was created + /// + public DateTime Time + { + get + { + return this._time; + } + } + + private static readonly DateTime UnixEpoch = + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private static long GetUnixTimestampMillis(DateTime dateTime) + { + return (long)(dateTime - UnixEpoch).TotalMilliseconds; + } + + private static DateTime DateTimeFromUnixTimestampMillis(long millis) + { + return UnixEpoch.AddMilliseconds(millis); + } + } +} diff --git a/src/PusherServer/WebHookEvent.cs b/src/PusherServer/WebHookEvent.cs new file mode 100644 index 0000000..8e70301 --- /dev/null +++ b/src/PusherServer/WebHookEvent.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer +{ + /// + /// A Web Hook Event + /// + public class WebHookEvent + { + } +} diff --git a/src/PusherServer/WebhookLevel.cs b/src/PusherServer/WebhookLevel.cs new file mode 100644 index 0000000..39aa0f9 --- /dev/null +++ b/src/PusherServer/WebhookLevel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PusherServer +{ + /// + /// Possible webhookLevels + /// + public enum WebhookLevel + { + /// + /// Info only output + /// + INFO, + /// + /// Debug information included + /// + DEBUG + } +} diff --git a/src/PusherServer/project.json b/src/PusherServer/project.json index a72ae08..cc6b8ce 100644 --- a/src/PusherServer/project.json +++ b/src/PusherServer/project.json @@ -5,10 +5,10 @@ "type": "platform" }, "Microsoft.AspNetCore.Diagnostics": "1.0.0", - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", - "Microsoft.Extensions.Logging.Console": "1.0.0" + "Microsoft.Extensions.Logging.Console": "1.0.0", + "RestSharp.NetCore": "105.2.3" }, "tools": { From ce1eb07b84562daf0166de274031f3a6c84aca7e Mon Sep 17 00:00:00 2001 From: JasonH Date: Tue, 20 Dec 2016 14:37:34 -0600 Subject: [PATCH 4/9] Fixed async notification url. --- src/PusherServer/Pusher.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PusherServer/Pusher.cs b/src/PusherServer/Pusher.cs index fa17c39..75882c1 100644 --- a/src/PusherServer/Pusher.cs +++ b/src/PusherServer/Pusher.cs @@ -501,7 +501,6 @@ private void ExecuteTriggerAsync(string path, object requestBody, Action callback) { - //_options.RestClient.BaseUrl = _options.GetBaseUrl(); _options.RestClient.BaseUrl = _options.GetBaseNotificationUrl(); var request = CreateAuthenticatedRequest(Method.POST, path, null, requestBody,true); @@ -510,7 +509,6 @@ private void ExecuteNotifyAsync(string path, object requestBody, Action Date: Tue, 20 Dec 2016 16:19:49 -0600 Subject: [PATCH 5/9] Removed old projects. --- .../AcceptanceTests/Authenticate.cs | 116 ---- .../AcceptanceTests/ChannelState.cs | 341 ---------- PusherServer.Tests/AcceptanceTests/Get.cs | 59 -- .../AcceptanceTests/PresenceChannels.cs | 228 ------- PusherServer.Tests/AcceptanceTests/Trigger.cs | 297 --------- .../AcceptanceTests/percent-message.json | 1 - PusherServer.Tests/App.config | 10 - PusherServer.Tests/Config.cs | 113 ---- .../Helpers/ClientServerFactory.cs | 53 -- .../Helpers/InMemoryAuthorizer.cs | 36 -- PusherServer.Tests/Properties/AssemblyInfo.cs | 36 -- PusherServer.Tests/PusherServer.Tests.csproj | 107 ---- PusherServer.Tests/UnitTests/Authenticate.cs | 184 ------ PusherServer.Tests/UnitTests/ChannelsList.cs | 38 -- PusherServer.Tests/UnitTests/Get.cs | 67 -- PusherServer.Tests/UnitTests/GetResult.cs | 43 -- PusherServer.Tests/UnitTests/Pusher.cs | 51 -- PusherServer.Tests/UnitTests/PusherOptions.cs | 221 ------- .../UnitTests/RawBodySerializer.cs | 49 -- PusherServer.Tests/UnitTests/Trigger.cs | 459 -------------- PusherServer.Tests/UnitTests/TriggerResult.cs | 79 --- PusherServer.Tests/UnitTests/WebHook.cs | 173 ----- PusherServer.Tests/packages.config | 11 - PusherServer/AuthenticationData.cs | 78 --- PusherServer/BatchEvent.cs | 28 - PusherServer/BatchTriggerBody.cs | 11 - PusherServer/ChannelsList.cs | 56 -- PusherServer/CryptoHelper.cs | 31 - PusherServer/DefaultDeserializer.cs | 16 - PusherServer/DefaultSerializer.cs | 16 - PusherServer/Event.cs | 28 - PusherServer/EventIdData.cs | 28 - .../Exceptions/NotifyResponseException.cs | 32 - .../Exceptions/TriggerResponseException.cs | 29 - PusherServer/GetResult.cs | 46 -- PusherServer/IAuthenticationData.cs | 24 - PusherServer/IDeserializeJsonStrings.cs | 15 - PusherServer/IGetResult.cs | 17 - PusherServer/INotifyResult.cs | 18 - PusherServer/IPusher.cs | 268 -------- PusherServer/IPusherOptions.cs | 77 --- PusherServer/IRequestResult.cs | 20 - PusherServer/ISerializeObjectsToJson.cs | 15 - PusherServer/ITriggerOptions.cs | 13 - PusherServer/ITriggerResult.cs | 15 - PusherServer/IWebHook.cs | 46 -- PusherServer/Notification.cs | 13 - PusherServer/NotifyBody.cs | 50 -- PusherServer/NotifyResult.cs | 45 -- PusherServer/NotifySubscriberData.cs | 18 - PusherServer/PresenceChannelData.cs | 23 - PusherServer/Properties/AssemblyInfo.cs | 38 -- PusherServer/Pusher.cs | 590 ------------------ PusherServer/PusherOptions.cs | 245 -------- PusherServer/PusherServer.csproj | 104 --- PusherServer/PusherServer.nuspec | 27 - PusherServer/RawBodySerializer.cs | 32 - PusherServer/RequestResult.cs | 66 -- PusherServer/TriggerBody.cs | 28 - PusherServer/TriggerOptions.cs | 14 - PusherServer/TriggerResult.cs | 45 -- PusherServer/Util/ReadOnlyDictionary.cs | 367 ----------- PusherServer/ValidationHelper.cs | 95 --- PusherServer/WebHook.cs | 114 ---- PusherServer/WebHookData.cs | 58 -- PusherServer/WebHookEvent.cs | 14 - PusherServer/WebhookLevel.cs | 22 - PusherServer/packages.config | 4 - 68 files changed, 5711 deletions(-) delete mode 100644 PusherServer.Tests/AcceptanceTests/Authenticate.cs delete mode 100644 PusherServer.Tests/AcceptanceTests/ChannelState.cs delete mode 100644 PusherServer.Tests/AcceptanceTests/Get.cs delete mode 100644 PusherServer.Tests/AcceptanceTests/PresenceChannels.cs delete mode 100644 PusherServer.Tests/AcceptanceTests/Trigger.cs delete mode 100644 PusherServer.Tests/AcceptanceTests/percent-message.json delete mode 100644 PusherServer.Tests/App.config delete mode 100644 PusherServer.Tests/Config.cs delete mode 100644 PusherServer.Tests/Helpers/ClientServerFactory.cs delete mode 100644 PusherServer.Tests/Helpers/InMemoryAuthorizer.cs delete mode 100644 PusherServer.Tests/Properties/AssemblyInfo.cs delete mode 100644 PusherServer.Tests/PusherServer.Tests.csproj delete mode 100644 PusherServer.Tests/UnitTests/Authenticate.cs delete mode 100644 PusherServer.Tests/UnitTests/ChannelsList.cs delete mode 100644 PusherServer.Tests/UnitTests/Get.cs delete mode 100644 PusherServer.Tests/UnitTests/GetResult.cs delete mode 100644 PusherServer.Tests/UnitTests/Pusher.cs delete mode 100644 PusherServer.Tests/UnitTests/PusherOptions.cs delete mode 100644 PusherServer.Tests/UnitTests/RawBodySerializer.cs delete mode 100644 PusherServer.Tests/UnitTests/Trigger.cs delete mode 100644 PusherServer.Tests/UnitTests/TriggerResult.cs delete mode 100644 PusherServer.Tests/UnitTests/WebHook.cs delete mode 100644 PusherServer.Tests/packages.config delete mode 100644 PusherServer/AuthenticationData.cs delete mode 100644 PusherServer/BatchEvent.cs delete mode 100644 PusherServer/BatchTriggerBody.cs delete mode 100644 PusherServer/ChannelsList.cs delete mode 100644 PusherServer/CryptoHelper.cs delete mode 100644 PusherServer/DefaultDeserializer.cs delete mode 100644 PusherServer/DefaultSerializer.cs delete mode 100644 PusherServer/Event.cs delete mode 100644 PusherServer/EventIdData.cs delete mode 100644 PusherServer/Exceptions/NotifyResponseException.cs delete mode 100644 PusherServer/Exceptions/TriggerResponseException.cs delete mode 100644 PusherServer/GetResult.cs delete mode 100644 PusherServer/IAuthenticationData.cs delete mode 100644 PusherServer/IDeserializeJsonStrings.cs delete mode 100644 PusherServer/IGetResult.cs delete mode 100644 PusherServer/INotifyResult.cs delete mode 100644 PusherServer/IPusher.cs delete mode 100644 PusherServer/IPusherOptions.cs delete mode 100644 PusherServer/IRequestResult.cs delete mode 100644 PusherServer/ISerializeObjectsToJson.cs delete mode 100644 PusherServer/ITriggerOptions.cs delete mode 100644 PusherServer/ITriggerResult.cs delete mode 100644 PusherServer/IWebHook.cs delete mode 100644 PusherServer/Notification.cs delete mode 100644 PusherServer/NotifyBody.cs delete mode 100644 PusherServer/NotifyResult.cs delete mode 100644 PusherServer/NotifySubscriberData.cs delete mode 100644 PusherServer/PresenceChannelData.cs delete mode 100644 PusherServer/Properties/AssemblyInfo.cs delete mode 100644 PusherServer/Pusher.cs delete mode 100644 PusherServer/PusherOptions.cs delete mode 100644 PusherServer/PusherServer.csproj delete mode 100644 PusherServer/PusherServer.nuspec delete mode 100644 PusherServer/RawBodySerializer.cs delete mode 100644 PusherServer/RequestResult.cs delete mode 100644 PusherServer/TriggerBody.cs delete mode 100644 PusherServer/TriggerOptions.cs delete mode 100644 PusherServer/TriggerResult.cs delete mode 100644 PusherServer/Util/ReadOnlyDictionary.cs delete mode 100644 PusherServer/ValidationHelper.cs delete mode 100644 PusherServer/WebHook.cs delete mode 100644 PusherServer/WebHookData.cs delete mode 100644 PusherServer/WebHookEvent.cs delete mode 100644 PusherServer/WebhookLevel.cs delete mode 100644 PusherServer/packages.config 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 d98a2bc..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 13e1104..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 c217495..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/AuthenticationData.cs b/PusherServer/AuthenticationData.cs deleted file mode 100644 index fd0eb49..0000000 --- a/PusherServer/AuthenticationData.cs +++ /dev/null @@ -1,78 +0,0 @@ -using RestSharp.Serializers; -using System.Runtime.Serialization; - -namespace PusherServer -{ - [DataContract] - class AuthenticationData: IAuthenticationData - { - private string _appKey; - private string _appSecret; - private string _channelName; - private string _socketId; - private PresenceChannelData _presenceData; - - public AuthenticationData(string appKey, string appSecret, string channelName, string socketId) - { - ValidationHelper.ValidateChannelName(channelName); - ValidationHelper.ValidateSocketId(socketId); - - _appKey = appKey; - _appSecret = appSecret; - _channelName = channelName; - _socketId = socketId; - } - - public AuthenticationData(string appKey, string appSecret, string channelName, string socketId, PresenceChannelData presenceData): - this(appKey, appSecret, channelName, socketId) - { - _presenceData = presenceData; - } - - [DataMember(Name = "auth", IsRequired = true)] - public string auth - { - get - { - var serializer = new JsonSerializer(); - var stringToSign = _socketId + ":" + _channelName; - if (_presenceData != null) - { - var presenceJson = serializer.Serialize(_presenceData); - stringToSign += ":" + presenceJson; - } - - return _appKey + ":" + CryptoHelper.GetHmac256(_appSecret, stringToSign); - } - } - - /// - /// Double encoded JSON containing presence channel user information. - /// - [DataMember(Name = "channel_data", IsRequired = false, EmitDefaultValue = false)] - public string channel_data - { - get - { - string json = null; - if (_presenceData != null) - { - var serializer = new JsonSerializer(); - json = serializer.Serialize(_presenceData); - } - return json; - } - } - - public string ToJson() - { - var serializer = new JsonSerializer(); - return serializer.Serialize(this); - } - - public override string ToString() - { - return ToJson(); - } - } -} diff --git a/PusherServer/BatchEvent.cs b/PusherServer/BatchEvent.cs deleted file mode 100644 index 20c22d9..0000000 --- a/PusherServer/BatchEvent.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace PusherServer -{ - /// - /// Represents an event that is part of a batch trigger, for the purposes of serialisation - /// - internal class BatchEvent - { - /// - /// The name of the event - /// - public string name { get; set; } - - /// - /// The event data - /// - public string data { get; set; } - - /// - /// The channel the event should be triggered on. - /// - public string channel { get; set; } - - /// - /// The id of a socket to be excluded from receiving the event. - /// - public string socket_id { get; set; } - } -} diff --git a/PusherServer/BatchTriggerBody.cs b/PusherServer/BatchTriggerBody.cs deleted file mode 100644 index 1ff07ef..0000000 --- a/PusherServer/BatchTriggerBody.cs +++ /dev/null @@ -1,11 +0,0 @@ - -namespace PusherServer -{ - /// - /// Represents the payload to be sent when triggering events - /// - class BatchTriggerBody - { - public BatchEvent[] batch { get; set; } - } -} diff --git a/PusherServer/ChannelsList.cs b/PusherServer/ChannelsList.cs deleted file mode 100644 index e91b504..0000000 --- a/PusherServer/ChannelsList.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Runtime.Serialization; -using System.Collections.Generic; - -namespace PusherServer -{ - /* - {"channels":{"test_channel":{}}} - */ - - /// - /// A list of Channels received from the Pusher Server - /// - [DataContract] - public class ChannelsList - { - private Dictionary> _channelsInfo = null; - - /// - /// Gets or sets the Channel Info for a given Channel Name - /// - /// - /// A string representing the Channel Info - public Dictionary this[string channelName] - { - get - { - return _channelsInfo[channelName]; - } - set - { - _channelsInfo[channelName] = value; - } - } - - /// - /// Gets or sets all the Channel Info - /// - [DataMember(Name = "channels")] - public Dictionary> Channels - { - get - { - return _channelsInfo; - } - set - { - if (_channelsInfo != null) - { - throw new InvalidOperationException("Channels should only be set as part of deserialization."); - } - _channelsInfo = value; - } - } - } -} diff --git a/PusherServer/CryptoHelper.cs b/PusherServer/CryptoHelper.cs deleted file mode 100644 index 883f708..0000000 --- a/PusherServer/CryptoHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - -namespace PusherServer -{ - internal class CryptoHelper - { - - internal static string GetMd5Hash(string jsonData) - { - var hash = new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(jsonData)); - return BytesToHex(hash); - } - - private static string BytesToHex(IEnumerable byteArray) - { - return String.Concat(byteArray.Select(bytes => bytes.ToString("x2")).ToArray()); - } - - internal static string GetHmac256(string secret, string toSign) - { - var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); - var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(toSign)); - - return BytesToHex(hash); - } - } -} diff --git a/PusherServer/DefaultDeserializer.cs b/PusherServer/DefaultDeserializer.cs deleted file mode 100644 index 96ec7f4..0000000 --- a/PusherServer/DefaultDeserializer.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Web.Script.Serialization; - -namespace PusherServer -{ - /// - /// Default implmentation for deserializing an object - /// - public class DefaultDeserializer : IDeserializeJsonStrings - { - /// - public T Deserialize(string stringToDeserialize) - { - return new JavaScriptSerializer().Deserialize(stringToDeserialize); - } - } -} \ No newline at end of file diff --git a/PusherServer/DefaultSerializer.cs b/PusherServer/DefaultSerializer.cs deleted file mode 100644 index 6076059..0000000 --- a/PusherServer/DefaultSerializer.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Web.Script.Serialization; - -namespace PusherServer -{ - /// - /// Default implmentation for serializing an object - /// - public class DefaultSerializer : ISerializeObjectsToJson - { - /// - public string Serialize(object objectToSerialize) - { - return new JavaScriptSerializer().Serialize(objectToSerialize); - } - } -} \ No newline at end of file diff --git a/PusherServer/Event.cs b/PusherServer/Event.cs deleted file mode 100644 index e431025..0000000 --- a/PusherServer/Event.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace PusherServer -{ - /// - /// Represents an event for batch submission - /// - public class Event - { - /// - /// The event name - /// - public string EventName { get; set; } - - /// - /// The channel to which the event should be sent - /// - public string Channel { get; set; } - - /// - /// An optional socket ID which should not receive the event - /// - public string SocketId { get; set; } - - /// - /// The event data - /// - public object Data { get; set; } - } -} diff --git a/PusherServer/EventIdData.cs b/PusherServer/EventIdData.cs deleted file mode 100644 index fbe7e85..0000000 --- a/PusherServer/EventIdData.cs +++ /dev/null @@ -1,28 +0,0 @@ - -using System.Collections.Generic; - -namespace PusherServer -{ - /// - /// Class used for handling the deserialisation of the Trigger HTTP response. - /// - public class EventIdData - { - private Dictionary _eventIds = new Dictionary(); - - /// - /// Dictionary of channel name to event ID for the triggered event. - /// - public Dictionary event_ids - { - get - { - return _eventIds; - } - set - { - _eventIds = value; - } - } - } -} \ No newline at end of file diff --git a/PusherServer/Exceptions/NotifyResponseException.cs b/PusherServer/Exceptions/NotifyResponseException.cs deleted file mode 100644 index f7cabd4..0000000 --- a/PusherServer/Exceptions/NotifyResponseException.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PusherServer.Exceptions -{ - /// - /// Thrown when problems are detected with the response from the Pusher Notify HTTP endpoint. - /// - public class NotifyResponseException : Exception - { - /// - /// Create a new instance - /// - /// Description of the exception - public NotifyResponseException(string message) : - base(message) - { - } - - /// - /// Create a new instance - /// - /// Description of the exception - /// The inner exception - public NotifyResponseException(string message, Exception innerException) : - base(message, innerException) - { - } - } -} diff --git a/PusherServer/Exceptions/TriggerResponseException.cs b/PusherServer/Exceptions/TriggerResponseException.cs deleted file mode 100644 index 1a5929c..0000000 --- a/PusherServer/Exceptions/TriggerResponseException.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace PusherServer.Exceptions -{ - /// - /// Thrown when problems are detected with the response from the Pusher trigger HTTP endpoint. - /// - public class TriggerResponseException : Exception - { - /// - /// Create a new instance - /// - /// Description of the exception - public TriggerResponseException(string message) : - base(message) - { - } - - /// - /// Create a new instance - /// - /// Description of the exception - /// The inner exception - public TriggerResponseException(string message, Exception innerException) : - base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/PusherServer/GetResult.cs b/PusherServer/GetResult.cs deleted file mode 100644 index df4e632..0000000 --- a/PusherServer/GetResult.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Net; -using RestSharp; - -namespace PusherServer -{ - /// - /// Deserialised the result from a Rest Response - /// - /// The Type the Rest Response contains - public class GetResult : RequestResult, IGetResult - { - /// - /// Attempts to deserialise the data contained with a Rest Response - /// - /// The response containing the data to deserialise - /// - public GetResult(IRestResponse response, IDeserializeJsonStrings deserializer) : base(response) - { - if (deserializer == null) - { - throw new ArgumentNullException("deserializer", "An instance of a deserializer needs to be provided"); - } - - DeserializeResponse(response, deserializer); - } - - /// - /// Gets the data deserialised from the Rest Response - /// - public T Data { get; private set; } - - private void DeserializeResponse(IRestResponse response, IDeserializeJsonStrings deserializer) - { - try - { - Data = deserializer.Deserialize(response.Content); - } - catch (Exception e) - { - StatusCode = HttpStatusCode.BadRequest; - Body = string.Format("The HTTP response could not be deserialized to the expected type. The following exception occurred: {0}", e); - } - } - } -} diff --git a/PusherServer/IAuthenticationData.cs b/PusherServer/IAuthenticationData.cs deleted file mode 100644 index 0d13c3c..0000000 --- a/PusherServer/IAuthenticationData.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace PusherServer -{ - /// - /// Interface for Authenticaton Data - /// - public interface IAuthenticationData - { - /// - /// Gets the Authetication String - /// - string auth { get; } - - /// - /// Double encoded JSON containing presence channel user information. - /// - string channel_data { get; } - - /// - /// Returns a Json representation of the authentication data. - /// - /// - string ToJson(); - } -} diff --git a/PusherServer/IDeserializeJsonStrings.cs b/PusherServer/IDeserializeJsonStrings.cs deleted file mode 100644 index fd362ba..0000000 --- a/PusherServer/IDeserializeJsonStrings.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace PusherServer -{ - /// - /// Contract that allows a JSON deserializer to be injected - /// - public interface IDeserializeJsonStrings - { - /// - /// Deserialize a JSON string into an object - /// - /// The JSON string to be deserialized into an object instance - /// A populated object - T Deserialize(string stringToDeserialize); - } -} \ No newline at end of file diff --git a/PusherServer/IGetResult.cs b/PusherServer/IGetResult.cs deleted file mode 100644 index 465bebd..0000000 --- a/PusherServer/IGetResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace PusherServer -{ - /// - /// The result of a GET HTTP request to the Pusher REST API. - /// - /// The object type that the data returned from the request should be deserialized to. - public interface IGetResult: IRequestResult - { - /// - /// Gets the data returned from the request in a deserialized form. - /// - /// - /// The data. - /// - T Data { get; } - } -} diff --git a/PusherServer/INotifyResult.cs b/PusherServer/INotifyResult.cs deleted file mode 100644 index 08985f8..0000000 --- a/PusherServer/INotifyResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PusherServer -{ - /// - /// Interface for Notify Request Results - /// - public interface INotifyResult : IRequestResult - { - /// - /// Number of subscribers - /// - int NumberOfSubscribers { get; } - } -} diff --git a/PusherServer/IPusher.cs b/PusherServer/IPusher.cs deleted file mode 100644 index 8854c61..0000000 --- a/PusherServer/IPusher.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System; - -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 interface IPusher - { - #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 - /// - /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON - /// - ITriggerResult Trigger(string channelName, string eventName, object 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 - /// - /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON - /// - ITriggerResult Trigger(string[] channelNames, string eventName, object data); - - /// - /// 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 - /// - /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON - /// - ITriggerResult Trigger(string channelName, string eventName, object data, ITriggerOptions options); - - /// - /// Triggers an event on the specified channels. - /// - /// The name 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. - /// Additional options to be used when triggering the event. See . - /// The result of the call to the REST API - /// - /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON - /// - ITriggerResult Trigger(string[] channelNames, string eventName, object data, ITriggerOptions options); - - /// - /// Triggers the events in the passed in array - /// - /// The events to trigger - /// The result of the call to the REST API - ITriggerResult Trigger(Event[] events); - - /// - /// Triggers an event on the specified channels in the background. - /// - /// The name of the channel to trigger the event on - /// The name of the event. - /// The data to be sent with the event. The event payload. - /// Method to call when the request has returned. - void TriggerAsync(string channelName, string eventName, object data, Action callback); - - /// - /// Triggers an event on the specified channels in the background. - /// - /// The name of the channel to trigger the event 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 . - /// Method to call when the request has returned. - void TriggerAsync(string channelName, string eventName, object data, ITriggerOptions options, Action callback); - - /// - /// Triggers an event on the specified channels in the background. - /// - /// The channels to trigger the event on - /// The name of the event. - /// The data to be sent with the event. The event payload. - /// Method to call when the request has returned. - void TriggerAsync(string[] channelNames, string eventName, object data, Action callback); - - /// - /// Triggers an event on the specified channels in the background. - /// - /// The channels to trigger the event 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 . - /// Method to call when the request has returned. - void TriggerAsync(string[] channelNames, string eventName, object data, ITriggerOptions options, Action callback); - - /// - /// Triggers the events in the passed in array asynchronously - /// - /// The events to trigger - /// Method to call when the request has returned. - /// The result of the call to the REST API - void TriggerAsync(Event[] events, Action callback); - - /// - /// Notification to native devices. - /// - /// array of interests that people should be subscribed to - /// The actual message to send in the APN - /// The title to send in the APN - /// The subtitle to send in the APN - /// These are used to specify an external URL to which Pusher will send information about the sent push notifications. This is particularly useful for debugging failed push notifications. - /// must be either "INFO" (errors-only) or "DEBUG" (everything). If omitted, it defaults to "INFO" - /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON - INotifyResult Notify(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level); - - /// - /// Notification to native devices, sincle interest - /// - /// Single interest we want to push to - /// The actual message to send in the APN - /// The title to send in the APN - /// The subtitle to send in the APN - /// These are used to specify an external URL to which Pusher will send information about the sent push notifications. This is particularly useful for debugging failed push notifications. - /// must be either "INFO" (errors-only) or "DEBUG" (everything). If omitted, it defaults to "INFO" - /// For any non-200 response from the HTTP API or if the response body cannot be parsed as JSON - INotifyResult Notify(string interest, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level); - - /// - /// Calls the notify api in an asynchronously fashion - /// - /// Single interest we want to push to - /// The actual message to send in the APN - /// The title to send in the APN - /// The subtitle to send in the APN - /// These are used to specify an external URL to which Pusher will send information about the sent push notifications. This is particularly useful for debugging failed push notifications. - /// must be either "INFO" (errors-only) or "DEBUG" (everything). If omitted, it defaults to "INFO" - /// Method to call when the request has returned - void NotifyAsync(string[] interests, string alertText, string alertTitle, string alertSubtitle, string webhook_url, WebhookLevel webhook_level, Action callback); - - #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 - IAuthenticationData Authenticate(string channelName, string 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 - IAuthenticationData Authenticate(string channelName, string socketId, PresenceChannelData data); - - #endregion - - /// - /// Makes a GET request to the specified resource. Authentication is handled as part of the call. The data returned from the request is deserizlized to the object type defined by . - /// - /// - /// The resource. - /// The result of the GET request - IGetResult Get(string resource); - - /// - /// Makes a GET request to the specified resource. Authentication is handled as part of the call. The data returned from the request is deserizlized to the object type defined by . - /// - /// - /// The resource. - /// Additional parameters to be sent as part of the request query string. - /// - /// The result of the GET request - /// - IGetResult Get(string resource, object parameters); - - /// - /// Handle an incoming WebHook and validate it. - /// - /// The signature of the incoming WebHook - /// The body of the incoming Webhook request - /// A WebHook helper. - IWebHook ProcessWebHook(string signature, string body); - - /// - /// Queries the Pusher API for the Users of a Presence Channel - /// - /// The type of object that will be returned by the API - /// The name of the channel to query - /// The result of the Presence Channel Users query - IGetResult FetchUsersFromPresenceChannel(string channelName); - - /// - /// Queries the Pusher API for the Users of a Presence Channel asynchronously - /// - /// The type of object that will be returned by the API - /// The name of the channel to query - /// The callback to receive the result of the query - void FetchUsersFromPresenceChannelAsync(string channelName, Action> callback); - - /// - /// Queries the Pusher API for the state of a Channel - /// - /// The type of object that will be returned by the API - /// The name of the channel to query - /// An object containing a list of attributes to include in the query - /// The result of the Channel State query - IGetResult FetchStateForChannel(string channelName, object info); - - /// - /// Queries the Pusher API for the state of a Channel asynchronously - /// - /// The type of object that will be returned by the API - /// The name of the channel to query - /// The callback to receive the result of the query - void FetchStateForChannelAsync(string channelName, Action> callback); - - /// - /// Queries the Pusher API for the state of a Channel asynchronously - /// - /// The type of object that will be returned by the API - /// The name of the channel to query - /// An object containing a list of attributes to include in the query - /// The callback to receive the result of the query - void FetchStateForChannelAsync(string channelName, object info, Action> callback); - - /// - /// Queries the Pusher API for the state of all channels based upon the info object - /// - /// The type of object that will be returned by the API - /// An object containing a list of attributes to include in the query - /// The result of the Channels State query - IGetResult FetchStateForChannels(object info); - - /// - /// Queries the Pusher API for the state of all channels based upon the info object - /// - /// The type of object that will be returned by the API - /// The callback to receive the result of the query - void FetchStateForChannelsAsync(Action> callback); - - /// - /// Queries the Pusher API for the state of all channels based upon the info object - /// - /// The type of object that will be returned by the API - /// An object containing a list of attributes to include in the query - /// The callback to receive the result of the query - void FetchStateForChannelsAsync(object info, Action> callback); - } -} \ No newline at end of file diff --git a/PusherServer/IPusherOptions.cs b/PusherServer/IPusherOptions.cs deleted file mode 100644 index 7888849..0000000 --- a/PusherServer/IPusherOptions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using RestSharp; - -namespace PusherServer -{ - /// - /// Interface for Pusher Options - /// - public interface IPusherOptions - { - /// - /// Gets or sets a value indicating whether calls to the Pusher REST API are over HTTP or HTTPS. - /// - /// - /// true if encrypted; otherwise, false. - /// - bool Encrypted { get; set; } - - /// - /// Gets or sets the REST API port that the HTTP calls will be made to. - /// - /// - /// The port. - /// - int Port { get; set; } - - /// - /// Gets or sets the Json Serializer - /// - ISerializeObjectsToJson JsonSerializer { get; set; } - - /// - /// Gets or sets the Json Deserializer - /// - IDeserializeJsonStrings JsonDeserializer { get; set; } - - /// - /// Gets or sets the rest client. Generally only expected to be used for testing. - /// - /// - /// The rest client. - /// - IRestClient RestClient { get; set; } - - /// - /// The host of the HTTP API endpoint excluding the scheme e.g. api.pusherapp.com - /// - /// If a scheme is found at the start of the host value - string HostName { get; set; } - - /// - /// The host of the HTTP Api endoind for push notifications e.g. nativepush-cluster1.pusher.com - /// - string HostName_Notificaiton { get; set; } - - /// - /// The vrsion of the the push notificaiton service e.g. v1 - /// - string Notificaiton_Version { get; set; } - - /// - /// The prefix to be used to form the notificaiton service url e.g. server_api - /// - string Notification_Prefix { get; set; } - - /// - /// The cluster where the application was created, e.g. eu - /// - string Cluster { get; set; } - - /// - /// Gets the base Url based on the set Options - /// - /// The constructed URL - Uri GetBaseUrl(); - } -} diff --git a/PusherServer/IRequestResult.cs b/PusherServer/IRequestResult.cs deleted file mode 100644 index ec19b89..0000000 --- a/PusherServer/IRequestResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Net; - -namespace PusherServer -{ - /// - /// Base interface for all Request Results - /// - public interface IRequestResult - { - /// - /// Gets the Body from a Request Result - /// - string Body { get; } - - /// - /// Gets the Status Code from a Request Result - /// - HttpStatusCode StatusCode { get; } - } -} diff --git a/PusherServer/ISerializeObjectsToJson.cs b/PusherServer/ISerializeObjectsToJson.cs deleted file mode 100644 index b2200c6..0000000 --- a/PusherServer/ISerializeObjectsToJson.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace PusherServer -{ - /// - /// Contract that allows a JSON serializer to be injected - /// - public interface ISerializeObjectsToJson - { - /// - /// Serialize an object - /// - /// The object to be serialized into a JSON string - /// The passed in object as a JSON string - string Serialize(object objectToSerialize); - } -} diff --git a/PusherServer/ITriggerOptions.cs b/PusherServer/ITriggerOptions.cs deleted file mode 100644 index 7cdeeff..0000000 --- a/PusherServer/ITriggerOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace PusherServer -{ - /// - /// Additional options that can be used when triggering an event. - /// - public interface ITriggerOptions - { - /// - /// Gets or sets the Socket ID for a consuming Trigger - /// - string SocketId { get; set; } - } -} diff --git a/PusherServer/ITriggerResult.cs b/PusherServer/ITriggerResult.cs deleted file mode 100644 index 458b9de..0000000 --- a/PusherServer/ITriggerResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace PusherServer -{ - /// - /// Interface for Trigger Request Results - /// - public interface ITriggerResult: IRequestResult - { - /// - /// Gets the Event IDs related to this Trigger Event - /// - IDictionary EventIds { get; } - } -} diff --git a/PusherServer/IWebHook.cs b/PusherServer/IWebHook.cs deleted file mode 100644 index e22a6bf..0000000 --- a/PusherServer/IWebHook.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PusherServer -{ - /// - /// Interface for Web Hooks - /// - public interface IWebHook - { - /// - /// Indicates if the WebHook has validated. - /// - bool IsValid - { - get; - } - - /// - /// The Events in the WebHook - /// - Dictionary[] Events - { - get; - } - - - /// - /// The timestamp of the WebHook - /// - DateTime Time - { - get; - } - - /// - /// An array of validation errors. If is true then the array - /// will have no elements. - /// - string[] ValidationErrors - { - get; - } - - } -} \ No newline at end of file diff --git a/PusherServer/Notification.cs b/PusherServer/Notification.cs deleted file mode 100644 index 2fdcfb4..0000000 --- a/PusherServer/Notification.cs +++ /dev/null @@ -1,13 +0,0 @@ -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/PusherServer/NotifyBody.cs deleted file mode 100644 index b38f4ef..0000000 --- a/PusherServer/NotifyBody.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PusherServer -{ - public class NotifyBody - { - public string[] interests { get; set; } - public Apns apns { get; set; } - public string webhook_url { get; set; } - public string webhook_level { get; set; } - - public NotifyBody() - { - apns = new Apns(); - } - } - - public class Apns - { - public Aps aps { get; set; } - - public Apns() - { - aps = new Aps(); - } - } - - public class Aps - { - public Alert alert { get; set; } - public Notification notification { get; set; } - - public Aps() - { - alert = new Alert(); - notification = new Notification(); - } - } - - public class Alert - { - public string body { get; set; } - public string title { get; set; } - public string subtitle { get; set; } - } - -} diff --git a/PusherServer/NotifyResult.cs b/PusherServer/NotifyResult.cs deleted file mode 100644 index 1dd3074..0000000 --- a/PusherServer/NotifyResult.cs +++ /dev/null @@ -1,45 +0,0 @@ -using PusherServer.Exceptions; -using RestSharp; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Script.Serialization; - -namespace PusherServer -{ - internal class NotifyResult : RequestResult, INotifyResult - { - private readonly int _numberOfSubscribers; - - /// - /// Constructs a new instance of a NotifyResult based upon a passed in Rest Response - /// - /// The Rest Response which will form the basis of this Notify Result - public NotifyResult(IRestResponse response) : base(response) - { - NotifySubscriberData subscriberData = null; - try - { - JavaScriptSerializer serializer = new JavaScriptSerializer(); - subscriberData = serializer.Deserialize(response.Content); - } - catch (Exception) - { - string msg = - string.Format("The response body from the Pusher HTTP endpoint could not be parsed as JSON: {0}{1}", - Environment.NewLine, - response.Content); - throw new NotifyResponseException(msg); - } - - _numberOfSubscribers = subscriberData.number_of_subscribers; - } - - /// - public int NumberOfSubscribers - { - get { return this._numberOfSubscribers; } - } - } -} diff --git a/PusherServer/NotifySubscriberData.cs b/PusherServer/NotifySubscriberData.cs deleted file mode 100644 index a05cce0..0000000 --- a/PusherServer/NotifySubscriberData.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PusherServer -{ - /// - /// Response payload from notify call - /// - public class NotifySubscriberData - { - /// - /// Number of subscribers to the listed events - /// - public int number_of_subscribers { get; set; } - } -} diff --git a/PusherServer/PresenceChannelData.cs b/PusherServer/PresenceChannelData.cs deleted file mode 100644 index ba27ed6..0000000 --- a/PusherServer/PresenceChannelData.cs +++ /dev/null @@ -1,23 +0,0 @@ - -namespace PusherServer -{ - /// - /// Information about a user who is subscribing to a presence channel. - /// - public class PresenceChannelData - { - /// - /// A unique user identifier for the user witin the application. - /// - /// - /// Pusher uses this to uniquely identify a user. So, if multiple users are given the same user_id - /// the second of these users will be ignored and won't be represented on the presence channel. - /// - public string user_id { get; set; } - - /// - /// Arbitrary additional information about the user. - /// - public object user_info { get; set; } - } -} 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/Pusher.cs b/PusherServer/Pusher.cs deleted file mode 100644 index a0b3ad9..0000000 --- a/PusherServer/Pusher.cs +++ /dev/null @@ -1,590 +0,0 @@ -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)); - } - } - } -} diff --git a/PusherServer/PusherOptions.cs b/PusherServer/PusherOptions.cs deleted file mode 100644 index ac8fe9c..0000000 --- a/PusherServer/PusherOptions.cs +++ /dev/null @@ -1,245 +0,0 @@ -using System; -using System.Text.RegularExpressions; -using RestSharp; - -namespace PusherServer -{ - /// - /// Options to be set on the Pusher instance. - /// - public class PusherOptions : IPusherOptions - { - /// - /// The default Rest API Host for contacting the Pusher server, it does not contain a cluster name - /// - public const string DEFAULT_REST_API_HOST = "api.pusherapp.com"; - /// - /// The default Notificaiton API Host for contacting the Noitificaiton server - /// - public const string DEFAULT_NOTIFICATION_REST_API_HOST = "nativepush-cluster1.pusher.com"; - - /// - /// The default version for the Notificaiton service - /// - public const string DEFAULT_NOTIFICATION_VERSION = "v1"; - - /// - /// The default prefix for the Notificaiton Service - /// - public const string DEFAULT_NOTIFICATION_PREFIX = "server_api"; - - private static int DEFAULT_HTTPS_PORT = 443; - private static int DEFAULT_HTTP_PORT = 80; - - IRestClient _client; - bool _encrypted = false; - bool _portModified = false; - bool _hostSet = false; - int _port = DEFAULT_HTTP_PORT; - string _hostName = null; - string _notificationHostName = null; - string _cluster = null; - string _notificationVersion = null; - string _notificationPrefix = null; - ISerializeObjectsToJson _jsonSerializer; - IDeserializeJsonStrings _jsonDeserializer; - - /// - public bool Encrypted - { - get - { - return _encrypted; - } - set - { - _encrypted = value; - if (_encrypted && _portModified == false) - { - _port = DEFAULT_HTTPS_PORT; - } - } - } - - /// - public int Port - { - get - { - return _port; - } - set - { - _port = value; - _portModified = true; - } - } - - /// - /// Set the cluster only if there is no custom host defined. - /// - public string Cluster - { - get - { - return _cluster; - } - set - { - if (_hostSet == false) { - _cluster = value; - _hostName = "api-"+_cluster+".pusher.com"; - } - } - } - - /// - public IRestClient RestClient - { - get - { - if (_client == null) - { - _client = new RestClient(GetBaseUrl()); - } - - return _client; - } - set { _client = value; } - } - - /// - /// Gets or sets the HostName to use in the base URL - /// - public string HostName - { - get - { - return _hostName ?? DEFAULT_REST_API_HOST; - } - set - { - if (Regex.IsMatch(value, "^.*://")) - { - string msg = string.Format("The scheme should not be present in the host value: {0}", value); - throw new FormatException(msg); - } - - _hostSet = true; - _cluster = null; - _hostName = value; - } - } - - /// - /// Gets or sets the HostName to use in the notificaiton base URL - /// - public string HostName_Notificaiton - { - get - { - return _notificationHostName ?? DEFAULT_NOTIFICATION_REST_API_HOST; - } - set - { - if (Regex.IsMatch(value, "^.*://")) - { - string msg = string.Format("The scheme should not be present in the host value: {0}", value); - throw new FormatException(msg); - } - - _hostSet = true; - _cluster = null; - _notificationHostName = value; - } - } - /// - /// The vrsion of the the push notificaiton service e.g. v1 - /// - public string Notificaiton_Version - { - get - { - return _notificationVersion ?? DEFAULT_NOTIFICATION_VERSION; - } - set - { - _notificationVersion = value; - } - } - - /// - /// The prefix to be used to form the notificaiton service url e.g. server_api - /// - public string Notification_Prefix - { - get - { - return _notificationPrefix ?? DEFAULT_NOTIFICATION_PREFIX; - } - set - { - - _notificationPrefix = value; - } - } - - /// - /// Gets or sets the Json Serializer - /// - public ISerializeObjectsToJson JsonSerializer - { - get - { - if (_jsonSerializer == null) - { - _jsonSerializer = new DefaultSerializer(); - } - - return _jsonSerializer; - - } - set { _jsonSerializer = value; } - } - - /// - /// Gets or sets the Json Deserializer - /// - public IDeserializeJsonStrings JsonDeserializer - { - get - { - if (_jsonDeserializer == null) - { - _jsonDeserializer = new DefaultDeserializer(); - } - - return _jsonDeserializer; - } - set - { - _jsonDeserializer = value; - } - } - - /// - public Uri GetBaseUrl() - { - string baseUrl = (Encrypted ? "https" : "http") + "://" + HostName;// + GetPort(); - - return new Uri(baseUrl); - } - - private string GetPort() - { - var port = string.Empty; - - if (Port != DEFAULT_HTTP_PORT) - { - port += (":" + Port); - } - - return port; - } - } -} diff --git a/PusherServer/PusherServer.csproj b/PusherServer/PusherServer.csproj deleted file mode 100644 index 46002aa..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/RawBodySerializer.cs b/PusherServer/RawBodySerializer.cs deleted file mode 100644 index 29b0d3d..0000000 --- a/PusherServer/RawBodySerializer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace PusherServer -{ - /// - /// An implementation of the that passes through the raw string. - /// - public class RawBodySerializer : ISerializeObjectsToJson - { - /// - /// Presumes we are getting a string as the body, and passes it through. - /// - /// The string body to pass through. - /// The body passed in. - /// Thrown if the body provided is not a string. - public string Serialize(object body) - { - if (body == null) - { - return string.Empty; - } - - var bodyAsString = body as string; - if(bodyAsString == null) - { - throw new ArgumentException("The RawBodySerializer only supports strings for messages. The body type was: " + body.GetType().Name, "body"); - } - - return bodyAsString; - } - } -} \ No newline at end of file diff --git a/PusherServer/RequestResult.cs b/PusherServer/RequestResult.cs deleted file mode 100644 index d70fa05..0000000 --- a/PusherServer/RequestResult.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Net; -using RestSharp; - -namespace PusherServer -{ - /// - /// Abstract base class for results coming back from request to the Pusher servers - /// - public abstract class RequestResult : IRequestResult - { - string _body = null; - private HttpStatusCode _statusCode; - - /// - /// Constructor to constract the abstract base class for classes derived from RequestResults - /// - /// - public RequestResult(IRestResponse response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - _body = response.Content; - _statusCode = response.StatusCode; - - Response = response; - } - - /// - /// Gets the Status Code returned in the wrapped Rest Response - /// - public HttpStatusCode StatusCode - { - get { return _statusCode; } - protected set { _statusCode = value; } - } - - /// - /// Gets the Body returned in the wrapped Rest Response - /// - public string Body - { - get { return _body; } - protected set { _body = value; } - } - - /// - /// Gets the original content that was returned in the response, if the response returned was Bad - /// - public string OriginalContent - { - get - { - return Response != null ? Response.Content : string.Empty; - } - } - - /// - /// Gets the original response from the rest service - /// - public IRestResponse Response { get; private set; } - } -} diff --git a/PusherServer/TriggerBody.cs b/PusherServer/TriggerBody.cs deleted file mode 100644 index d858144..0000000 --- a/PusherServer/TriggerBody.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace PusherServer -{ - /// - /// Represents the payload to be sent when triggering events - /// - internal class TriggerBody - { - /// - /// The name of the event - /// - public string name { get; set; } - - /// - /// The event data - /// - public string data { get; set; } - - /// - /// The channels the event should be triggered on. - /// - public string[] channels { get; set; } - - /// - /// The id of a socket to be excluded from receiving the event. - /// - public string socket_id { get; set; } - } -} diff --git a/PusherServer/TriggerOptions.cs b/PusherServer/TriggerOptions.cs deleted file mode 100644 index a222973..0000000 --- a/PusherServer/TriggerOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ - -namespace PusherServer -{ - /// - /// Represents the Options that can be used by A Trigger - /// - public class TriggerOptions: ITriggerOptions - { - /// - /// Gets or sets the Socket ID for the consuming Trigger - /// - public string SocketId { get; set; } - } -} diff --git a/PusherServer/TriggerResult.cs b/PusherServer/TriggerResult.cs deleted file mode 100644 index 1aafbb7..0000000 --- a/PusherServer/TriggerResult.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Net; -using System.Collections.Generic; -using System.Web.Script.Serialization; -using PusherServer.Exceptions; -using PusherServer.Util; -using RestSharp; - -namespace PusherServer -{ - internal class TriggerResult : RequestResult, ITriggerResult - { - private readonly IDictionary _eventIds; - - /// - /// Constructs a new instance of a TriggerResult based upon a passed in Rest Response - /// - /// The Rest Response which will form the basis of this Trigger Result - public TriggerResult(IRestResponse response) : base(response) - { - EventIdData eventIdData = null; - try - { - JavaScriptSerializer serializer = new JavaScriptSerializer(); - eventIdData = serializer.Deserialize(response.Content); - } - catch (Exception) - { - string msg = - string.Format("The response body from the Pusher HTTP endpoint could not be parsed as JSON: {0}{1}", - Environment.NewLine, - response.Content); - throw new TriggerResponseException(msg); - } - - _eventIds = new ReadOnlyDictionary(eventIdData.event_ids); - } - - /// - public IDictionary EventIds - { - get { return this._eventIds; } - } - } -} \ No newline at end of file diff --git a/PusherServer/Util/ReadOnlyDictionary.cs b/PusherServer/Util/ReadOnlyDictionary.cs deleted file mode 100644 index c51cd9d..0000000 --- a/PusherServer/Util/ReadOnlyDictionary.cs +++ /dev/null @@ -1,367 +0,0 @@ -namespace PusherServer.Util -{ - // From: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=29 - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics; - using System.Runtime.InteropServices; - using System.Threading; - - /// - /// Provides the base class for a generic read-only dictionary. - /// - /// - /// The type of keys in the dictionary. - /// - /// - /// The type of values in the dictionary. - /// - /// - /// - /// An instance of the ReadOnlyDictionary generic class is - /// always read-only. A dictionary that is read-only is simply a - /// dictionary with a wrapper that prevents modifying the - /// dictionary; therefore, if changes are made to the underlying - /// dictionary, the read-only dictionary reflects those changes. - /// See for a modifiable version of - /// this class. - /// - /// - /// Notes to Implementers This base class is provided to - /// make it easier for implementers to create a generic read-only - /// custom dictionary. Implementers are encouraged to extend this - /// base class instead of creating their own. - /// - /// - [Serializable] - [DebuggerDisplay("Count = {Count}")] - [ComVisible(false)] - [DebuggerTypeProxy(typeof(ReadOnlyDictionaryDebugView<,>))] - public class ReadOnlyDictionary : IDictionary, - ICollection - { - private readonly IDictionary source; - private object syncRoot; - - /// - /// Initializes a new instance of the - /// class that wraps - /// the supplied . - /// - /// The - /// that will be wrapped. - /// - /// Thrown when the dictionary is null. - /// - public ReadOnlyDictionary(IDictionary dictionaryToWrap) - { - if (dictionaryToWrap == null) - { - throw new ArgumentNullException("dictionaryToWrap"); - } - - this.source = dictionaryToWrap; - } - - /// - /// Gets the number of key/value pairs contained in the - /// . - /// - /// The number of key/value pairs. - /// The number of key/value pairs contained in the - /// . - public int Count - { - get { return this.source.Count; } - } - - /// Gets a collection containing the keys in the - /// . - /// A - /// containing the keys. - /// A - /// - /// containing the keys in the - /// . - /// - public ICollection Keys - { - get { return this.source.Keys; } - } - - /// - /// Gets a collection containing the values of the - /// . - /// - /// The collection of values. - public ICollection Values - { - get { return this.source.Values; } - } - - /// Gets a value indicating whether the dictionary is read-only. - /// This value will always be true. - bool ICollection>.IsReadOnly - { - get { return true; } - } - - /// - /// Gets a value indicating whether access to the dictionary - /// is synchronized (thread safe). - /// - bool ICollection.IsSynchronized - { - get { return false; } - } - - /// - /// Gets an object that can be used to synchronize access to dictionary. - /// - object ICollection.SyncRoot - { - get - { - if (this.syncRoot == null) - { - ICollection collection = this.source as ICollection; - - if (collection != null) - { - this.syncRoot = collection.SyncRoot; - } - else - { - Interlocked.CompareExchange(ref this.syncRoot, new object(), null); - } - } - - return this.syncRoot; - } - } - - /// - /// Gets or sets the value associated with the specified key. - /// - /// - /// The value associated with the specified key. If the specified key - /// is not found, a get operation throws a - /// , - /// and a set operation creates a new element with the specified key. - /// - /// The key of the value to get or set. - /// - /// Thrown when the key is null. - /// - /// - /// The property is retrieved and key does not exist in the collection. - /// - public TValue this[TKey key] - { - get { return this.source[key]; } - set { ThrowNotSupportedException(); } - } - - /// This method is not supported by the - /// . - /// - /// The object to use as the key of the element to add. - /// - /// The object to use as the value of the element to add. - void IDictionary.Add(TKey key, TValue value) - { - ThrowNotSupportedException(); - } - - /// Determines whether the - /// contains the specified key. - /// - /// True if the contains - /// an element with the specified key; otherwise, false. - /// - /// The key to locate in the - /// . - /// - /// Thrown when the key is null. - /// - public bool ContainsKey(TKey key) - { - return this.source.ContainsKey(key); - } - - /// - /// This method is not supported by the . - /// - /// The key of the element to remove. - /// - /// True if the element is successfully removed; otherwise, false. - /// - bool IDictionary.Remove(TKey key) - { - ThrowNotSupportedException(); - return false; - } - - /// - /// Gets the value associated with the specified key. - /// - /// The key of the value to get. - /// When this method returns, contains the value - /// associated with the specified key, if the key is found; - /// otherwise, the default value for the type of the value parameter. - /// This parameter is passed uninitialized. - /// - /// true if the contains - /// an element with the specified key; otherwise, false. - /// - public bool TryGetValue(TKey key, out TValue value) - { - return this.source.TryGetValue(key, out value); - } - - /// This method is not supported by the - /// . - /// - /// The object to add to the . - /// - void ICollection>.Add( - KeyValuePair item) - { - ThrowNotSupportedException(); - } - - /// This method is not supported by the - /// . - void ICollection>.Clear() - { - ThrowNotSupportedException(); - } - - /// - /// Determines whether the contains a - /// specific value. - /// - /// - /// The object to locate in the . - /// - /// - /// true if item is found in the ICollection; - /// otherwise, false. - /// - bool ICollection>.Contains( - KeyValuePair item) - { - ICollection> collection = this.source; - - return collection.Contains(item); - } - - /// - /// Copies the elements of the ICollection to an Array, starting at a - /// particular Array index. - /// - /// The one-dimensional Array that is the - /// destination of the elements copied from ICollection. - /// The Array must have zero-based indexing. - /// - /// - /// The zero-based index in array at which copying begins. - /// - void ICollection>.CopyTo( - KeyValuePair[] array, int arrayIndex) - { - ICollection> collection = this.source; - collection.CopyTo(array, arrayIndex); - } - - /// This method is not supported by the - /// . - /// - /// The object to remove from the ICollection. - /// - /// Will never return a value. - bool ICollection>.Remove(KeyValuePair item) - { - ThrowNotSupportedException(); - return false; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A IEnumerator that can be used to iterate through the collection. - /// - IEnumerator> - IEnumerable>.GetEnumerator() - { - IEnumerable> enumerator = this.source; - - return enumerator.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An IEnumerator that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.source.GetEnumerator(); - } - - /// - /// For a description of this member, see . - /// - /// - /// The one-dimensional Array that is the destination of the elements copied from - /// ICollection. The Array must have zero-based indexing. - /// - /// - /// The zero-based index in Array at which copying begins. - /// - void ICollection.CopyTo(Array array, int index) - { - ICollection collection = - new List>(this.source); - - collection.CopyTo(array, index); - } - - private static void ThrowNotSupportedException() - { - throw new NotSupportedException("This Dictionary is read-only"); - } - } - - internal sealed class ReadOnlyDictionaryDebugView - { - private IDictionary dict; - - public ReadOnlyDictionaryDebugView( - ReadOnlyDictionary dictionary) - { - if (dictionary == null) - { - throw new ArgumentNullException("dictionary"); - } - - this.dict = dictionary; - } - - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Items - { - get - { - KeyValuePair[] array = - new KeyValuePair[this.dict.Count]; - this.dict.CopyTo(array, 0); - return array; - } - } - } -} \ No newline at end of file diff --git a/PusherServer/ValidationHelper.cs b/PusherServer/ValidationHelper.cs deleted file mode 100644 index aa87dc5..0000000 --- a/PusherServer/ValidationHelper.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace PusherServer -{ - /// - /// Helps validation of channel names and socket_id values. - /// - public static class ValidationHelper - { - /// - /// A regular expression to check that a channel name is in a format allowed and accepted by Pusher. - /// - public static Regex CHANNEL_NAME_REGEX = new Regex(@"\A[a-zA-Z0-9_=@,.;\-]+\z", RegexOptions.Singleline); - - /// - /// The maximum length of a channel name allowed by Pusher. - /// - public static int CHANNEL_NAME_MAX_LENGTH = 164; - - /// - /// A regular expression to check that a socket_id is in a format allowed and accepted by Pusher. - /// - public static Regex SOCKET_ID_REGEX = new Regex(@"\A\d+\.\d+\z", RegexOptions.Singleline); - - /// - /// The maximum event batch size accepted by Pusher - /// - public static int MAX_BATCH_SIZE = 100; - - /// - /// Validate a socket_id value - /// - /// The value to be checked. - /// If the socket_id name is not in the allowed format. - internal static void ValidateSocketId(string socketId) - { - if (socketId != null && SOCKET_ID_REGEX.IsMatch(socketId) == false) - { - string msg = - string.Format("socket_id \"{0}\" was not in the form: {1}", socketId, SOCKET_ID_REGEX.ToString()); - throw new FormatException(msg); - } - } - - /// - /// Validate a single channel name is in the allowed format. - /// - /// The channel name to be checked - /// If the channel name is not in the allowed format. - internal static void ValidateChannelName(string channelName) - { - if(channelName.Length > CHANNEL_NAME_MAX_LENGTH) - { - string msg = - string.Format("The length of the channel name was greater than the allowed {0} characters", CHANNEL_NAME_MAX_LENGTH); - throw new ArgumentOutOfRangeException(msg); - } - - if (CHANNEL_NAME_REGEX.IsMatch(channelName) == false) - { - string msg = - string.Format("channel name \"{0}\" was not in the form: {1}", channelName, CHANNEL_NAME_REGEX.ToString()); - throw new FormatException(msg); - } - } - - /// - /// Validate an array of channel names - /// - /// The array of channel names - /// If any channel names are not in the allowed format. - internal static void ValidateChannelNames(string[] channelNames) - { - foreach(string name in channelNames) - { - ValidateChannelName(name); - } - } - - internal static void ValidateBatchEvents(Event[] events) - { - if (events.Length > 100) - { - throw new ArgumentOutOfRangeException(string.Format("Only {0} events permitted per batch, {1} submitted", MAX_BATCH_SIZE, events.Length)); - } - - foreach (Event e in events) - { - ValidateChannelName(e.Channel); - ValidateSocketId(e.SocketId); - } - } - } -} diff --git a/PusherServer/WebHook.cs b/PusherServer/WebHook.cs deleted file mode 100644 index bda98a9..0000000 --- a/PusherServer/WebHook.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace PusherServer -{ - internal class WebHook: IWebHook - { - private readonly WebHookData _webHookData; - private readonly List _validationErrors; - private IDeserializeJsonStrings _jsonDeserializer; - - internal WebHook(string secret, string signature, string body) - { - if(string.IsNullOrEmpty(secret)) - { - throw new ArgumentException("A secret must be provided", "secret"); - } - - this._validationErrors = new List(); - - this._webHookData = ValidateWebHook(secret, signature, body); - } - - private WebHookData ValidateWebHook(string secret, string signature, string body) - { - WebHookData parsedWebHookData = null; - - var signatureNullOrEmpth = string.IsNullOrEmpty(signature); - if(signatureNullOrEmpth == true) { - this._validationErrors.Add("The supplied signature to check was null or empty. A signature to check must be provided."); - } - - if (string.IsNullOrEmpty(body) == true) - { - this._validationErrors.Add("The supplied body to check was null or empty. A body to check must be provided."); - } - else - { - try - { - parsedWebHookData = JsonDeserializer.Deserialize(body); - } - catch (Exception e) - { - var validationError = string.Format("Exception occurred parsing the body as JSON: {0}{1}", Environment.NewLine, e.StackTrace); - this._validationErrors.Add(validationError); - } - } - - if (parsedWebHookData != null) - { - var expectedSignature = CryptoHelper.GetHmac256(secret, body); - var signatureIsValid = (signature == expectedSignature); - if (signatureIsValid == false) - { - this._validationErrors.Add( - string.Format("The signature did not validate. Expected {0}. Got {1}", signature, expectedSignature) - ); - } - } - return parsedWebHookData; - } - - public bool IsValid - { - get - { - return (this.ValidationErrors.Length == 0); - } - } - - public Dictionary[] Events - { - get - { - return this._webHookData.events; - } - } - - public DateTime Time - { - get - { - return this._webHookData.Time; - } - } - - public string[] ValidationErrors - { - get - { - return this._validationErrors.ToArray(); - } - } - - /// - /// Gets or sets the JSON Deserializer to use - /// - public IDeserializeJsonStrings JsonDeserializer - { - get - { - if (_jsonDeserializer == null) - { - _jsonDeserializer = new DefaultDeserializer(); - } - - return _jsonDeserializer; - } - set { _jsonDeserializer = value; } - } - } -} diff --git a/PusherServer/WebHookData.cs b/PusherServer/WebHookData.cs deleted file mode 100644 index 9cf1ae9..0000000 --- a/PusherServer/WebHookData.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PusherServer -{ - /// - /// Represents the Data payload of a Web Hook - /// - public class WebHookData - { - private DateTime _time; - - /// - /// Gets or sets the Time the Web Hook was created in Milliseconds - /// - public string time_ms - { - get - { - // This should not be used. - return GetUnixTimestampMillis(this.Time).ToString(); - } - set - { - long unixTimeStamp = long.Parse(value); - _time = DateTimeFromUnixTimestampMillis(unixTimeStamp); - } - } - /// - /// Gets or sets the Events being triggered - /// - public Dictionary[] events { get; set; } - - /// - /// Gets the Time the Web Hook was created - /// - public DateTime Time - { - get - { - return this._time; - } - } - - private static readonly DateTime UnixEpoch = - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - private static long GetUnixTimestampMillis(DateTime dateTime) - { - return (long)(dateTime - UnixEpoch).TotalMilliseconds; - } - - private static DateTime DateTimeFromUnixTimestampMillis(long millis) - { - return UnixEpoch.AddMilliseconds(millis); - } - } -} diff --git a/PusherServer/WebHookEvent.cs b/PusherServer/WebHookEvent.cs deleted file mode 100644 index 8e70301..0000000 --- a/PusherServer/WebHookEvent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PusherServer -{ - /// - /// A Web Hook Event - /// - public class WebHookEvent - { - } -} diff --git a/PusherServer/WebhookLevel.cs b/PusherServer/WebhookLevel.cs deleted file mode 100644 index 39aa0f9..0000000 --- a/PusherServer/WebhookLevel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PusherServer -{ - /// - /// Possible webhookLevels - /// - public enum WebhookLevel - { - /// - /// Info only output - /// - INFO, - /// - /// Debug information included - /// - DEBUG - } -} 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 From 70c7fa0c3858cec9efd41c2fd9d9498b4baf49dd Mon Sep 17 00:00:00 2001 From: JasonH Date: Tue, 20 Dec 2016 16:22:12 -0600 Subject: [PATCH 6/9] Removed old solution file. --- pusher-dotnet-server.sln | 55 ---------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 pusher-dotnet-server.sln diff --git a/pusher-dotnet-server.sln b/pusher-dotnet-server.sln deleted file mode 100644 index a6870e9..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 From 250e31906c37595bca71c9138a1a98a6c9157b90 Mon Sep 17 00:00:00 2001 From: JasonH Date: Tue, 9 May 2017 12:07:35 -0500 Subject: [PATCH 7/9] build works --- global.json | 6 ---- pusher-dotnetcore-server.sln | 20 ++++++------ src/PusherServer/PusherServer.csproj | 29 +++++++++++++++++ src/PusherServer/PusherServer.xproj | 25 --------------- src/PusherServer/project.json | 48 ---------------------------- 5 files changed, 40 insertions(+), 88 deletions(-) delete mode 100644 global.json create mode 100644 src/PusherServer/PusherServer.csproj delete mode 100644 src/PusherServer/PusherServer.xproj delete mode 100644 src/PusherServer/project.json diff --git a/global.json b/global.json deleted file mode 100644 index 9d09ab5..0000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "projects": [ "src", "test" ], - "sdk": { - "version": "1.0.0-preview2-003131" - } -} diff --git a/pusher-dotnetcore-server.sln b/pusher-dotnetcore-server.sln index a5599a0..a22f6d3 100644 --- a/pusher-dotnetcore-server.sln +++ b/pusher-dotnetcore-server.sln @@ -1,27 +1,29 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# 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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{31D1F264-8216-45B7-A4E1-2A545E32CAB5}" - ProjectSection(SolutionItems) = preProject - global.json = global.json - EndProjectSection -EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PusherServer", "src\PusherServer\PusherServer.xproj", "{6D75B971-15AA-4CBB-A160-886A524724AD}" +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 diff --git a/src/PusherServer/PusherServer.csproj b/src/PusherServer/PusherServer.csproj new file mode 100644 index 0000000..80c101c --- /dev/null +++ b/src/PusherServer/PusherServer.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp1.0 + true + PusherServer + Exe + PusherServer + 1.0.4 + $(PackageTargetFallback);dotnet5.6;portable-net45+win8 + false + false + false + false + false + false + false + false + + + + + + + + + + + diff --git a/src/PusherServer/PusherServer.xproj b/src/PusherServer/PusherServer.xproj deleted file mode 100644 index cbf7bfb..0000000 --- a/src/PusherServer/PusherServer.xproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 6d75b971-15aa-4cbb-a160-886a524724ad - PusherServer - .\obj - .\bin\ - v4.5.2 - - - - 2.0 - - - - - - - diff --git a/src/PusherServer/project.json b/src/PusherServer/project.json deleted file mode 100644 index cc6b8ce..0000000 --- a/src/PusherServer/project.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.1", - "type": "platform" - }, - "Microsoft.AspNetCore.Diagnostics": "1.0.0", - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "RestSharp.NetCore": "105.2.3" - }, - - "tools": { - "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" - }, - - "frameworks": { - "netcoreapp1.0": { - "imports": [ - "dotnet5.6", - "portable-net45+win8" - ] - } - }, - - "buildOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true - }, - - "runtimeOptions": { - "configProperties": { - "System.GC.Server": true - } - }, - - "publishOptions": { - "include": [ - "wwwroot", - "web.config" - ] - }, - - "scripts": { - "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] - } -} From 05e12462576931a59a00315be3601a35c34e7b02 Mon Sep 17 00:00:00 2001 From: JasonH Date: Tue, 9 May 2017 13:41:50 -0500 Subject: [PATCH 8/9] Migrated and added missing properties on Notification. --- src/PusherServer/Notification.cs | 3 ++- src/PusherServer/PusherServer.csproj | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/PusherServer/Notification.cs b/src/PusherServer/Notification.cs index 0233f1e..bd1370d 100644 --- a/src/PusherServer/Notification.cs +++ b/src/PusherServer/Notification.cs @@ -5,7 +5,8 @@ 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/src/PusherServer/PusherServer.csproj b/src/PusherServer/PusherServer.csproj index 80c101c..ffe23c9 100644 --- a/src/PusherServer/PusherServer.csproj +++ b/src/PusherServer/PusherServer.csproj @@ -1,7 +1,7 @@ - + - netcoreapp1.0 + netcoreapp1.1 true PusherServer Exe From 425327bf06b405aff8160d37757e5d9a43f2dfb5 Mon Sep 17 00:00:00 2001 From: JasonH Date: Mon, 17 Jul 2017 10:19:26 -0500 Subject: [PATCH 9/9] project file change --- src/PusherServer/PusherServer.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PusherServer/PusherServer.csproj b/src/PusherServer/PusherServer.csproj index ffe23c9..990add1 100644 --- a/src/PusherServer/PusherServer.csproj +++ b/src/PusherServer/PusherServer.csproj @@ -19,10 +19,10 @@ - - - - + + + +