diff --git a/.gitignore b/.gitignore index 85c57a9d2..1aafb3eaf 100644 --- a/.gitignore +++ b/.gitignore @@ -170,7 +170,10 @@ src/Examples/PubnubApiPCL.XamariniMacExample/obj/* *.suo src/UnitTests/MockServer/bin/* src/UnitTests/MockServer/obj/* - +src/PNEventEngine/bin/* +src/PNEventEngine/obj/* +src/PNEventEngineUWP/bin/* +src/PNEventEngineUWP/obj/* # GitHub Actions # ################## diff --git a/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs b/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs index a4a614a01..062014892 100644 --- a/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs +++ b/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Globalization; using System.Threading.Tasks; using System.Threading; diff --git a/src/Api/PubnubApi/ClientNetworkStatus.cs b/src/Api/PubnubApi/ClientNetworkStatus.cs index 15bcc07a7..d706e6b9a 100644 --- a/src/Api/PubnubApi/ClientNetworkStatus.cs +++ b/src/Api/PubnubApi/ClientNetworkStatus.cs @@ -209,7 +209,7 @@ private static async Task CheckSocketConnect(object internetState) pubnubCallback = state.PubnubCallbacck; } - PubnubApi.Interface.IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(pubnubConfig, jsonLib, unit, pubnubLog, null, null, ""); + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(pubnubConfig, jsonLib, unit, pubnubLog, null, null, ""); Uri requestUri = urlBuilder.BuildTimeRequest("GET", "", null); try { diff --git a/src/Api/PubnubApi/EndPoint/Access/AuditOperation.cs b/src/Api/PubnubApi/EndPoint/Access/AuditOperation.cs index c951ccde4..2e4368e52 100644 --- a/src/Api/PubnubApi/EndPoint/Access/AuditOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Access/AuditOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Access/GrantOperation.cs b/src/Api/PubnubApi/EndPoint/Access/GrantOperation.cs index e460b150e..d11eab87b 100644 --- a/src/Api/PubnubApi/EndPoint/Access/GrantOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Access/GrantOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Access/GrantTokenOperation.cs b/src/Api/PubnubApi/EndPoint/Access/GrantTokenOperation.cs index ca79df9a2..5ab257582 100644 --- a/src/Api/PubnubApi/EndPoint/Access/GrantTokenOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Access/GrantTokenOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Access/RevokeTokenOperation.cs b/src/Api/PubnubApi/EndPoint/Access/RevokeTokenOperation.cs index 8ac4defa2..cddae2111 100644 --- a/src/Api/PubnubApi/EndPoint/Access/RevokeTokenOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Access/RevokeTokenOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/src/Api/PubnubApi/EndPoint/ChannelGroup/AddChannelsToChannelGroupOperation.cs b/src/Api/PubnubApi/EndPoint/ChannelGroup/AddChannelsToChannelGroupOperation.cs index 6ffdcb49d..e5b382245 100644 --- a/src/Api/PubnubApi/EndPoint/ChannelGroup/AddChannelsToChannelGroupOperation.cs +++ b/src/Api/PubnubApi/EndPoint/ChannelGroup/AddChannelsToChannelGroupOperation.cs @@ -1,5 +1,4 @@ using System; -using PubnubApi.Interface; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/src/Api/PubnubApi/EndPoint/ChannelGroup/DeleteChannelGroupOperation.cs b/src/Api/PubnubApi/EndPoint/ChannelGroup/DeleteChannelGroupOperation.cs index 3c2fe1d69..af125b23f 100644 --- a/src/Api/PubnubApi/EndPoint/ChannelGroup/DeleteChannelGroupOperation.cs +++ b/src/Api/PubnubApi/EndPoint/ChannelGroup/DeleteChannelGroupOperation.cs @@ -1,5 +1,4 @@ using System; -using PubnubApi.Interface; using System.Collections.Generic; using System.Threading.Tasks; using System.Threading; diff --git a/src/Api/PubnubApi/EndPoint/ChannelGroup/ListAllChannelGroupOperation.cs b/src/Api/PubnubApi/EndPoint/ChannelGroup/ListAllChannelGroupOperation.cs index c5f2e7cca..36409c2c4 100644 --- a/src/Api/PubnubApi/EndPoint/ChannelGroup/ListAllChannelGroupOperation.cs +++ b/src/Api/PubnubApi/EndPoint/ChannelGroup/ListAllChannelGroupOperation.cs @@ -1,5 +1,4 @@ using System; -using PubnubApi.Interface; using System.Collections.Generic; using System.Threading.Tasks; using System.Threading; diff --git a/src/Api/PubnubApi/EndPoint/ChannelGroup/ListChannelsForChannelGroupOperation.cs b/src/Api/PubnubApi/EndPoint/ChannelGroup/ListChannelsForChannelGroupOperation.cs index 0d075fdd2..e939d0a03 100644 --- a/src/Api/PubnubApi/EndPoint/ChannelGroup/ListChannelsForChannelGroupOperation.cs +++ b/src/Api/PubnubApi/EndPoint/ChannelGroup/ListChannelsForChannelGroupOperation.cs @@ -1,5 +1,4 @@ using System; -using PubnubApi.Interface; using System.Collections.Generic; using System.Threading.Tasks; using System.Threading; diff --git a/src/Api/PubnubApi/EndPoint/ChannelGroup/RemoveChannelsFromChannelGroupOperation.cs b/src/Api/PubnubApi/EndPoint/ChannelGroup/RemoveChannelsFromChannelGroupOperation.cs index 2ae9a71c6..cc6a2c8a1 100644 --- a/src/Api/PubnubApi/EndPoint/ChannelGroup/RemoveChannelsFromChannelGroupOperation.cs +++ b/src/Api/PubnubApi/EndPoint/ChannelGroup/RemoveChannelsFromChannelGroupOperation.cs @@ -1,5 +1,4 @@ using System; -using PubnubApi.Interface; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/src/Api/PubnubApi/EndPoint/Files/DeleteFileOperation.cs b/src/Api/PubnubApi/EndPoint/Files/DeleteFileOperation.cs index 473360c42..e24165d97 100644 --- a/src/Api/PubnubApi/EndPoint/Files/DeleteFileOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Files/DeleteFileOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Api/PubnubApi/EndPoint/Files/DownloadFileOperation.cs b/src/Api/PubnubApi/EndPoint/Files/DownloadFileOperation.cs index 9e5b11c6b..93896da53 100644 --- a/src/Api/PubnubApi/EndPoint/Files/DownloadFileOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Files/DownloadFileOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; diff --git a/src/Api/PubnubApi/EndPoint/Files/GenerateFileUploadUrlOperation.cs b/src/Api/PubnubApi/EndPoint/Files/GenerateFileUploadUrlOperation.cs index a6bc1bdf6..bed5b7551 100644 --- a/src/Api/PubnubApi/EndPoint/Files/GenerateFileUploadUrlOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Files/GenerateFileUploadUrlOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Files/GetFileUrlOperation.cs b/src/Api/PubnubApi/EndPoint/Files/GetFileUrlOperation.cs index d45e933d2..15f71572a 100644 --- a/src/Api/PubnubApi/EndPoint/Files/GetFileUrlOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Files/GetFileUrlOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Api/PubnubApi/EndPoint/Files/ListFilesOperation.cs b/src/Api/PubnubApi/EndPoint/Files/ListFilesOperation.cs index 0fee0bbb2..68f5b264b 100644 --- a/src/Api/PubnubApi/EndPoint/Files/ListFilesOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Files/ListFilesOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Api/PubnubApi/EndPoint/Files/PublishFileMessage.cs b/src/Api/PubnubApi/EndPoint/Files/PublishFileMessage.cs index 07ba49634..5fc623dee 100644 --- a/src/Api/PubnubApi/EndPoint/Files/PublishFileMessage.cs +++ b/src/Api/PubnubApi/EndPoint/Files/PublishFileMessage.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Files/SendFileOperation.cs b/src/Api/PubnubApi/EndPoint/Files/SendFileOperation.cs index 8b90596be..b6101ed69 100644 --- a/src/Api/PubnubApi/EndPoint/Files/SendFileOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Files/SendFileOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; diff --git a/src/Api/PubnubApi/EndPoint/Objects/GetAllChannelMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/GetAllChannelMetadataOperation.cs index cca22bac0..530c21fcf 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/GetAllChannelMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/GetAllChannelMetadataOperation.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using PubnubApi.Interface; using System.Threading; using System.Net; using System.Threading.Tasks; diff --git a/src/Api/PubnubApi/EndPoint/Objects/GetAllUuidMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/GetAllUuidMetadataOperation.cs index 172c59b60..9770d9f3c 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/GetAllUuidMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/GetAllUuidMetadataOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/GetChannelMembersOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/GetChannelMembersOperation.cs index 419759fbb..c4e52998e 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/GetChannelMembersOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/GetChannelMembersOperation.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using PubnubApi.Interface; using System.Threading; using System.Net; using System.Threading.Tasks; diff --git a/src/Api/PubnubApi/EndPoint/Objects/GetChannelMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/GetChannelMetadataOperation.cs index 88f066483..0abc78dfd 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/GetChannelMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/GetChannelMetadataOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/GetMembershipsOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/GetMembershipsOperation.cs index f1450d494..6ffd8db66 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/GetMembershipsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/GetMembershipsOperation.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using PubnubApi.Interface; using System.Threading; using System.Net; using System.Threading.Tasks; diff --git a/src/Api/PubnubApi/EndPoint/Objects/GetUuidMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/GetUuidMetadataOperation.cs index dff659736..ebb029bab 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/GetUuidMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/GetUuidMetadataOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/ManageChannelMembersOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/ManageChannelMembersOperation.cs index 7eb4d9ae1..82002c736 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/ManageChannelMembersOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/ManageChannelMembersOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/ManageMembershipsOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/ManageMembershipsOperation.cs index bc04ad27b..134f81e59 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/ManageMembershipsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/ManageMembershipsOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMembersOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMembersOperation.cs index 6361ea3f6..3cfa60ced 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMembersOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMembersOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMetadataOperation.cs index 0c19b796c..7529a38e2 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/RemoveChannelMetadataOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/RemoveMembershipsOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/RemoveMembershipsOperation.cs index a8a46db94..6cde2d341 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/RemoveMembershipsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/RemoveMembershipsOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/RemoveUuidMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/RemoveUuidMetadataOperation.cs index bea1d6bc5..c68183d31 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/RemoveUuidMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/RemoveUuidMetadataOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/SetChannelMembersOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/SetChannelMembersOperation.cs index ddd149486..0f0cdb95c 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/SetChannelMembersOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/SetChannelMembersOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/SetChannelMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/SetChannelMetadataOperation.cs index 1752faed5..7a54951a1 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/SetChannelMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/SetChannelMetadataOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/SetMembershipsOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/SetMembershipsOperation.cs index 3709893a0..34c0c6495 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/SetMembershipsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/SetMembershipsOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Objects/SetUuidMetadataOperation.cs b/src/Api/PubnubApi/EndPoint/Objects/SetUuidMetadataOperation.cs index bb20c5e20..539378e5d 100644 --- a/src/Api/PubnubApi/EndPoint/Objects/SetUuidMetadataOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Objects/SetUuidMetadataOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/OtherOperation.cs b/src/Api/PubnubApi/EndPoint/OtherOperation.cs index 75f6dbf6e..67e7efb8b 100644 --- a/src/Api/PubnubApi/EndPoint/OtherOperation.cs +++ b/src/Api/PubnubApi/EndPoint/OtherOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Presence/GetStateOperation.cs b/src/Api/PubnubApi/EndPoint/Presence/GetStateOperation.cs index 6fdfbaf58..d3673c217 100644 --- a/src/Api/PubnubApi/EndPoint/Presence/GetStateOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Presence/GetStateOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Presence/HereNowOperation.cs b/src/Api/PubnubApi/EndPoint/Presence/HereNowOperation.cs index 793fcb913..98c96482f 100644 --- a/src/Api/PubnubApi/EndPoint/Presence/HereNowOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Presence/HereNowOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Presence/SetStateOperation.cs b/src/Api/PubnubApi/EndPoint/Presence/SetStateOperation.cs index b70d58c2b..3889f196a 100644 --- a/src/Api/PubnubApi/EndPoint/Presence/SetStateOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Presence/SetStateOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Presence/WhereNowOperation.cs b/src/Api/PubnubApi/EndPoint/Presence/WhereNowOperation.cs index d6b91774a..e8056d7e4 100644 --- a/src/Api/PubnubApi/EndPoint/Presence/WhereNowOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Presence/WhereNowOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/AddMessageActionOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/AddMessageActionOperation.cs index df77271f1..926036467 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/AddMessageActionOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/AddMessageActionOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/FireOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/FireOperation.cs index 21fb7a902..e51c10e30 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/FireOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/FireOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/GetMessageActionsOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/GetMessageActionsOperation.cs index befffa145..163c30bf1 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/GetMessageActionsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/GetMessageActionsOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/ListenerManager.cs b/src/Api/PubnubApi/EndPoint/PubSub/ListenerManager.cs index 29ddac66b..f31dfcf5e 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/ListenerManager.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/ListenerManager.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text; using System.Threading; -using PubnubApi.Interface; using System.Globalization; #if !NET35 && !NET40 using System.Collections.Concurrent; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/PublishOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/PublishOperation.cs index 5a2dbdea5..de3e175f1 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/PublishOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/PublishOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/RemoveMessageActionOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/RemoveMessageActionOperation.cs index 26b28631b..e7da08f46 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/RemoveMessageActionOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/RemoveMessageActionOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SignalOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/SignalOperation.cs index ee1f92acc..ba2610a85 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SignalOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SignalOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager.cs index ef3305bb3..01801cbe9 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using PubnubApi.Interface; using System.Net; using System.Threading.Tasks; using System.Globalization; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs new file mode 100644 index 000000000..a84db0422 --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs @@ -0,0 +1,637 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Net; +using System.Threading.Tasks; +using System.Globalization; +using System.Collections; +using System.Text; +using PubnubApi.EventEngine.Subscribe.Common; +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 +using System.Net.Http; +using System.Net.Http.Headers; +#endif + +namespace PubnubApi.EndPoint +{ + internal class SubscribeManager2 : IDisposable + { + private PNConfiguration config; + private IJsonPluggableLibrary jsonLibrary; + private IPubnubUnitTest unit; + private IPubnubLog pubnubLog; + private EndPoint.TelemetryManager pubnubTelemetryMgr; + private IPubnubHttp pubnubHttp; + + private Timer SubscribeHeartbeatCheckTimer; +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + private HttpClient httpSubscribe { get; set; } + private HttpClient httpNonsubscribe { get; set; } + private HttpClient httpNetworkStatus { get; set; } + private PubnubHttpClientHandler pubnubHttpClientHandler { get; set; } +#else + private HttpWebRequest httpSubscribe { get; set; } +#endif + public SubscribeManager2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) + { + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + //PubnubInstance = instance; + +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + if (httpSubscribe == null) + { + if (config.Proxy != null) + { + HttpClientHandler httpClientHandler = new HttpClientHandler(); + if (httpClientHandler.SupportsProxy) + { + httpClientHandler.Proxy = config.Proxy; + httpClientHandler.UseProxy = true; + } + pubnubHttpClientHandler = new PubnubHttpClientHandler("PubnubHttpClientHandler", httpClientHandler, config, jsonLibrary, unit, log); + httpSubscribe = new HttpClient(pubnubHttpClientHandler); + } + else + { + httpSubscribe = new HttpClient(); + } + httpSubscribe.DefaultRequestHeaders.Accept.Clear(); + httpSubscribe.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpSubscribe.Timeout = TimeSpan.FromSeconds(config.SubscribeTimeout); + } + if (httpNonsubscribe == null) + { + if (config.Proxy != null) + { + HttpClientHandler httpClientHandler = new HttpClientHandler(); + if (httpClientHandler.SupportsProxy) + { + httpClientHandler.Proxy = config.Proxy; + httpClientHandler.UseProxy = true; + } + pubnubHttpClientHandler = new PubnubHttpClientHandler("PubnubHttpClientHandler", httpClientHandler, config, jsonLibrary, unit, log); + httpNonsubscribe = new HttpClient(pubnubHttpClientHandler); + } + else + { + httpNonsubscribe = new HttpClient(); + } + httpNonsubscribe.DefaultRequestHeaders.Accept.Clear(); + httpNonsubscribe.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpNonsubscribe.Timeout = TimeSpan.FromSeconds(config.NonSubscribeRequestTimeout); + } + pubnubHttp = new PubnubHttp(config, jsonLibrary, log, pubnubTelemetryMgr, httpSubscribe, httpNonsubscribe); +#else + pubnubHttp = new PubnubHttp(config, jsonLibrary, log, pubnubTelemetryMgr); +#endif + } + +#pragma warning disable + + public async Task> HandshakeRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + { + string channelsJsonState = BuildJsonUserState(channels, channelGroups, false); + + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, null, ""); + Uri request = urlBuilder.BuildMultiChannelSubscribeRequest("GET", "", channels, channelGroups, timetoken.GetValueOrDefault(), region.GetValueOrDefault(), channelsJsonState, initialSubscribeUrlParams, externalQueryParam); + + RequestState pubnubRequestState = new RequestState(); + pubnubRequestState.Channels = channels; + pubnubRequestState.ChannelGroups = channelGroups; + pubnubRequestState.ResponseType = responseType; + pubnubRequestState.Timetoken = timetoken.GetValueOrDefault(); + pubnubRequestState.Region = region.GetValueOrDefault(); + pubnubRequestState.TimeQueued = DateTime.Now; + + Tuple responseTuple = await UrlProcessRequest(request, pubnubRequestState, false).ConfigureAwait(false); + if (!string.IsNullOrEmpty(responseTuple.Item1) && responseTuple.Item2 == null) + { + PNStatus status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, channels, channelGroups); + HandshakeResponse handshakeResponse = jsonLibrary.DeserializeToObject(responseTuple.Item1); + return new Tuple(handshakeResponse, status); + } + + return new Tuple(null, responseTuple.Item2); + } + + internal void HandshakeRequestCancellation() + { + if (httpSubscribe != null) + { + try + { + #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + httpSubscribe.CancelPendingRequests(); + #else + httpSubscribe.Abort(); + #endif + httpSubscribe = null; + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => HandshakeRequestCancellation. Done.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => HandshakeRequestCancellation Exception: {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), config.LogVerbosity); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => HandshakeRequestCancellation. No request to cancel.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + } + } + internal async Task, PNStatus>> ReceiveRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + { + Tuple, PNStatus> resp = new Tuple, PNStatus>(null, null); + + try + { + string channelsJsonState = BuildJsonUserState(channels, channelGroups, false); + + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, null, ""); + Uri request = urlBuilder.BuildMultiChannelSubscribeRequest("GET", "", channels, channelGroups, timetoken.GetValueOrDefault(), region.GetValueOrDefault(), channelsJsonState, initialSubscribeUrlParams, externalQueryParam); + + RequestState> pubnubRequestState = new RequestState>(); + pubnubRequestState.Channels = channels; + pubnubRequestState.ChannelGroups = channelGroups; + pubnubRequestState.ResponseType = responseType; + //pubnubRequestState.Reconnect = reconnect; + pubnubRequestState.Timetoken = timetoken.GetValueOrDefault(); + pubnubRequestState.Region = region.GetValueOrDefault(); + pubnubRequestState.TimeQueued = DateTime.Now; + + // Wait for message + var responseTuple = await UrlProcessRequest(request, pubnubRequestState, false).ConfigureAwait(false); + if (!string.IsNullOrEmpty(responseTuple.Item1) && responseTuple.Item2 == null) + { + PNStatus status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, channels, channelGroups); + ReceivingResponse receiveResponse = jsonLibrary.DeserializeToObject>(responseTuple.Item1); + return new Tuple, PNStatus>(receiveResponse, status); + } + return new Tuple, PNStatus>(null, responseTuple.Item2); + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager=> MultiChannelSubscribeInit \n channel(s)={1} \n cg(s)={2} \n Exception Details={3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), string.Join(",", channels.OrderBy(x => x).ToArray()), string.Join(",", channelGroups.OrderBy(x => x).ToArray()), ex), config.LogVerbosity); + } + return resp; + } + + internal void ReceiveRequestCancellation() + { + if (httpSubscribe != null) + { + try + { + #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + httpSubscribe.CancelPendingRequests(); + #else + httpSubscribe.Abort(); + #endif + httpSubscribe = null; + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => ReceiveRequestCancellation. Done.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => ReceiveRequestCancellation Exception: {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), config.LogVerbosity); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => RequestCancellation. No request to cancel.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + } + } + + internal void ReceiveReconnectRequestCancellation() + { + if (httpSubscribe != null) + { + try + { + #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + httpSubscribe.CancelPendingRequests(); + #else + httpSubscribe.Abort(); + #endif + httpSubscribe = null; + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => ReceiveReconnectRequestCancellation. Done.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => ReceiveReconnectRequestCancellation Exception: {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), config.LogVerbosity); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => ReceiveReconnectRequestCancellation. No request to cancel.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + } + } + + internal protected async Task> UrlProcessRequest(Uri requestUri, RequestState pubnubRequestState, bool terminateCurrentSubRequest) + { + return await UrlProcessRequest(requestUri, pubnubRequestState, terminateCurrentSubRequest, null).ConfigureAwait(false); + } + +#pragma warning disable + internal protected async Task> UrlProcessRequest(Uri requestUri, RequestState pubnubRequestState, bool terminateCurrentSubRequest, byte[] postOrPatchData) +#pragma warning restore + { + return await UrlProcessRequest(requestUri, pubnubRequestState, terminateCurrentSubRequest, postOrPatchData,"").ConfigureAwait(false); + } + + internal protected async Task> UrlProcessRequest(Uri requestUri, RequestState pubnubRequestState, bool terminateCurrentSubRequest, byte[] postOrPatchData, string contentType) + { + string channel = ""; + PNConfiguration currentConfig; + IPubnubLog currentLog; + + try + { + if (pubnubRequestState != null) + { + channel = (pubnubRequestState.Channels != null && pubnubRequestState.Channels.Length > 0) ? string.Join(",", pubnubRequestState.Channels.OrderBy(x => x).ToArray()) : ","; + + } + +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + //do nothing +#else + // Create Request + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri); + request = pubnubHttp.SetServicePointConnectionLimit(pubnubRequestState, request); + request = pubnubHttp.SetNoCache(request); + request = pubnubHttp.SetProxy(request); + request = pubnubHttp.SetTimeout(pubnubRequestState, request); + request = pubnubHttp.SetServicePointSetTcpKeepAlive(pubnubRequestState, request); + request = pubnubHttp.SetTcpKeepAlive(request); + if (string.IsNullOrEmpty(contentType)) + { + contentType = "application/json"; + } + request.ContentType = contentType; + + pubnubRequestState.Request = request; + httpSubscribe = request; +#endif + + string jsonString = ""; +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + if (pubnubRequestState != null && pubnubRequestState.UsePostMethod) + { + jsonString = await pubnubHttp.SendRequestAndGetJsonResponseWithPOST(requestUri, pubnubRequestState, null, postOrPatchData, contentType).ConfigureAwait(false); + } + else if (pubnubRequestState != null && pubnubRequestState.UsePatchMethod) + { + jsonString = await pubnubHttp.SendRequestAndGetJsonResponseWithPATCH(requestUri, pubnubRequestState, null, postOrPatchData, contentType).ConfigureAwait(false); + } + else + { + jsonString = await pubnubHttp.SendRequestAndGetJsonResponse(requestUri, pubnubRequestState, null).ConfigureAwait(false); + } +#else + if (pubnubRequestState != null && pubnubRequestState.UsePostMethod) + { + jsonString = await pubnubHttp.SendRequestAndGetJsonResponseWithPOST(requestUri, pubnubRequestState, request, postOrPatchData, contentType).ConfigureAwait(false); + } + else if (pubnubRequestState != null && pubnubRequestState.UsePatchMethod) + { + jsonString = await pubnubHttp.SendRequestAndGetJsonResponseWithPATCH(requestUri, pubnubRequestState, request, postOrPatchData, contentType).ConfigureAwait(false); + } + else + { + jsonString = await pubnubHttp.SendRequestAndGetJsonResponse(requestUri, pubnubRequestState, request).ConfigureAwait(false); + } +#endif + + PNStatus errStatus = GetStatusIfError(pubnubRequestState, jsonString); + return new Tuple((errStatus == null) ? jsonString : "", errStatus); + } + catch (Exception ex) + { + string errorMessage = ex.Message; + string exceptionMessage = ""; + Exception innerEx = null; + WebException webEx = null; + PNStatus status = null; + + if (ex.InnerException != null) + { + if (ex is WebException) + { + webEx = ex as WebException; + exceptionMessage = webEx.ToString(); + } + else + { + innerEx = ex.InnerException; + exceptionMessage = innerEx.ToString(); + } + } + else + { + innerEx = ex; + exceptionMessage = innerEx.ToString(); + } + + if (exceptionMessage.IndexOf("The request was aborted: The request was canceled", StringComparison.CurrentCultureIgnoreCase) == -1 + && exceptionMessage.IndexOf("Machine suspend mode enabled. No request will be processed.", StringComparison.CurrentCultureIgnoreCase) == -1 + && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation && exceptionMessage.IndexOf("The operation has timed out", StringComparison.CurrentCultureIgnoreCase) == -1) + && exceptionMessage.IndexOf("A task was canceled", StringComparison.CurrentCultureIgnoreCase) == -1 + && errorMessage.IndexOf("The operation was canceled", StringComparison.CurrentCultureIgnoreCase) == -1) + { + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(webEx == null ? innerEx : webEx); + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(pubnubRequestState.ResponseType, category, pubnubRequestState, (int)HttpStatusCode.NotFound, new PNException(ex)); + //if (pubnubRequestState != null && pubnubRequestState.PubnubCallback != null) + //{ + // pubnubRequestState.PubnubCallback.OnResponse(default(T), status); + //} + //else + //{ + // Announce(status); + //} + + //if (PubnubInstance != null && pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + //{ + // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} PubnubBaseCore UrlProcessRequest Exception={1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), webEx != null ? webEx.ToString() : exceptionMessage), currentConfig.LogVerbosity); + //} + } + + return new Tuple("", status); + } + } + + protected string BuildJsonUserState(string channel, string channelGroup, bool local) + { + Dictionary channelUserStateDictionary = null; + Dictionary channelGroupUserStateDictionary = null; + + if (!string.IsNullOrEmpty(channel) && !string.IsNullOrEmpty(channelGroup)) + { + throw new ArgumentException("BuildJsonUserState takes either channel or channelGroup at one time. Send one at a time by passing empty value for other."); + } + + StringBuilder jsonStateBuilder = new StringBuilder(); + + if (channelUserStateDictionary != null) + { + string[] channelUserStateKeys = channelUserStateDictionary.Keys.ToArray(); + + for (int keyIndex = 0; keyIndex < channelUserStateKeys.Length; keyIndex++) + { + string channelUserStateKey = channelUserStateKeys[keyIndex]; + object channelUserStateValue = channelUserStateDictionary[channelUserStateKey]; + if (channelUserStateValue == null) + { + jsonStateBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\":{1}", channelUserStateKey, string.Format(CultureInfo.InvariantCulture, "\"{0}\"", "null")); + } + else if (channelUserStateValue.GetType().ToString() == "System.Boolean") + { + jsonStateBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\":{1}", channelUserStateKey, channelUserStateValue.ToString().ToLowerInvariant()); + } + else + { + jsonStateBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\":{1}", channelUserStateKey, (channelUserStateValue.GetType().ToString() == "System.String") ? string.Format(CultureInfo.InvariantCulture, "\"{0}\"", channelUserStateValue) : channelUserStateValue); + } + if (keyIndex < channelUserStateKeys.Length - 1) + { + jsonStateBuilder.Append(','); + } + } + } + if (channelGroupUserStateDictionary != null) + { + string[] channelGroupUserStateKeys = channelGroupUserStateDictionary.Keys.ToArray(); + + for (int keyIndex = 0; keyIndex < channelGroupUserStateKeys.Length; keyIndex++) + { + string channelGroupUserStateKey = channelGroupUserStateKeys[keyIndex]; + object channelGroupUserStateValue = channelGroupUserStateDictionary[channelGroupUserStateKey]; + if (channelGroupUserStateValue == null) + { + jsonStateBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\":{1}", channelGroupUserStateKey, string.Format(CultureInfo.InvariantCulture, "\"{0}\"", "null")); + } + else if (channelGroupUserStateValue.GetType().ToString() == "System.Boolean") + { + jsonStateBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\":{1}", channelGroupUserStateKey, channelGroupUserStateValue.ToString().ToLowerInvariant()); + } + else + { + jsonStateBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\":{1}", channelGroupUserStateKey, (channelGroupUserStateValue.GetType().ToString() == "System.String") ? string.Format(CultureInfo.InvariantCulture, "\"{0}\"", channelGroupUserStateValue) : channelGroupUserStateValue); + } + if (keyIndex < channelGroupUserStateKeys.Length - 1) + { + jsonStateBuilder.Append(','); + } + } + } + + return jsonStateBuilder.ToString(); + } + + protected string BuildJsonUserState(string[] channels, string[] channelGroups, bool local) + { + string retJsonUserState = ""; + + StringBuilder jsonStateBuilder = new StringBuilder(); + + if (channels != null && channels.Length > 0) + { + for (int index = 0; index < channels.Length; index++) + { + string currentJsonState = BuildJsonUserState(channels[index], "", local); + if (!string.IsNullOrEmpty(currentJsonState)) + { + currentJsonState = string.Format(CultureInfo.InvariantCulture, "\"{0}\":{{{1}}}", channels[index], currentJsonState); + if (jsonStateBuilder.Length > 0) + { + jsonStateBuilder.Append(','); + } + jsonStateBuilder.Append(currentJsonState); + } + } + } + + if (channelGroups != null && channelGroups.Length > 0) + { + for (int index = 0; index < channelGroups.Length; index++) + { + string currentJsonState = BuildJsonUserState("", channelGroups[index], local); + if (!string.IsNullOrEmpty(currentJsonState)) + { + currentJsonState = string.Format(CultureInfo.InvariantCulture, "\"{0}\":{{{1}}}", channelGroups[index], currentJsonState); + if (jsonStateBuilder.Length > 0) + { + jsonStateBuilder.Append(','); + } + jsonStateBuilder.Append(currentJsonState); + } + } + } + + if (jsonStateBuilder.Length > 0) + { + retJsonUserState = string.Format(CultureInfo.InvariantCulture, "{{{0}}}", jsonStateBuilder); + } + + return retJsonUserState; + } + + private PNStatus GetStatusIfError(RequestState asyncRequestState, string jsonString) + { + PNStatus status = null; + if (string.IsNullOrEmpty(jsonString)) { return status; } + + PNConfiguration currentConfig; + PNOperationType type = PNOperationType.None; + if (asyncRequestState != null) + { + type = asyncRequestState.ResponseType; + } + if (jsonLibrary.IsDictionaryCompatible(jsonString, type)) + { + Dictionary deserializeStatus = jsonLibrary.DeserializeToDictionaryOfObject(jsonString); + int statusCode = 0; //default. assuming all is ok + if (deserializeStatus.Count >= 1 && deserializeStatus.ContainsKey("error") && string.Equals(deserializeStatus["error"].ToString(), "true", StringComparison.OrdinalIgnoreCase)) + { + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, PNStatusCategory.PNUnknownCategory, asyncRequestState, (int)HttpStatusCode.NotFound, new PNException(jsonString)); + } + else if (deserializeStatus.Count >= 1 && deserializeStatus.ContainsKey("error") && deserializeStatus.ContainsKey("status") && Int32.TryParse(deserializeStatus["status"].ToString(), out statusCode) && statusCode > 0) + { + string errorMessageJson = deserializeStatus["error"].ToString(); + Dictionary errorDic = jsonLibrary.DeserializeToDictionaryOfObject(errorMessageJson); + if (errorDic != null && errorDic.Count > 0 && errorDic.ContainsKey("message") + && statusCode != 200) + { + string statusMessage = errorDic["message"].ToString(); + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(statusCode, statusMessage); + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, asyncRequestState, statusCode, new PNException(jsonString)); + } + } + else if (deserializeStatus.Count >= 1 && deserializeStatus.ContainsKey("status") && string.Equals(deserializeStatus["status"].ToString(), "error", StringComparison.OrdinalIgnoreCase) && deserializeStatus.ContainsKey("error")) + { + string errorMessageJson = deserializeStatus["error"].ToString(); + Dictionary errorDic = jsonLibrary.DeserializeToDictionaryOfObject(errorMessageJson); + if (errorDic != null && errorDic.Count > 0 && errorDic.ContainsKey("code") && errorDic.ContainsKey("message")) + { + statusCode = PNStatusCodeHelper.GetHttpStatusCode(errorDic["code"].ToString()); + string statusMessage = errorDic["message"].ToString(); + if (statusCode != 200) + { + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(statusCode, statusMessage); + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, asyncRequestState, statusCode, new PNException(jsonString)); + } + } + } + else if (deserializeStatus.ContainsKey("status") && deserializeStatus.ContainsKey("message")) + { + var _ = Int32.TryParse(deserializeStatus["status"].ToString(), out statusCode); + string statusMessage = deserializeStatus["message"].ToString(); + + if (statusCode != 200) + { + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(statusCode, statusMessage); + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, asyncRequestState, statusCode, new PNException(jsonString)); + } + } + + } + else if (jsonString.ToLowerInvariant().TrimStart().IndexOf("(type, PNStatusCategory.PNNetworkIssuesCategory, asyncRequestState, (int)HttpStatusCode.NotFound, new PNException(jsonString)); + } + else if (jsonString.ToLowerInvariant().TrimStart().IndexOf("(type, PNStatusCategory.PNNetworkIssuesCategory, asyncRequestState, (int)HttpStatusCode.NotFound, new PNException(jsonString)); + } + + return status; + } + + internal List WrapResultBasedOnResponseType(PNOperationType type, string jsonString, string[] channels, string[] channelGroups) + { + List result = new List(); + try + { + string multiChannel = (channels != null) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ""; + string multiChannelGroup = (channelGroups != null) ? string.Join(",", channelGroups.OrderBy(x => x).ToArray()) : ""; + + if (!string.IsNullOrEmpty(jsonString)) + { + object deserializedResult = jsonLibrary.DeserializeToObject(jsonString); + List result1 = ((IEnumerable)deserializedResult).Cast().ToList(); + + if (result1 != null && result1.Count > 0) + { + result = result1; + } + + switch (type) + { + case PNOperationType.PNSubscribeOperation: + case PNOperationType.Presence: + if (result.Count == 3 && result[0] is object[] && (result[0] as object[]).Length == 0 && result[2].ToString() == "") + { + result.RemoveAt(2); + } + if (result.Count == 4 && result[0] is object[] && (result[0] as object[]).Length == 0 && result[2].ToString() == "" && result[3].ToString() == "") + { + result.RemoveRange(2, 2); + } + result.Add(multiChannelGroup); + result.Add(multiChannel); + + break; + case PNOperationType.PNHeartbeatOperation: + //Dictionary heartbeatadictionary = jsonLibrary.DeserializeToDictionaryOfObject(jsonString); + //result = new List(); + //result.Add(heartbeatadictionary); + //result.Add(multiChannel); + break; + default: + break; + } + //switch stmt end + } + } + catch { /* ignore */ } + + return result; + } + + + internal bool Disconnect() + { + return true; + } + + + #region IDisposable Support + private bool disposedValue; + + protected virtual void DisposeInternal(bool disposing) + { + if (!disposedValue) + { + if (SubscribeHeartbeatCheckTimer != null) + { + SubscribeHeartbeatCheckTimer.Dispose(); + } + + disposedValue = true; + } + } + + void IDisposable.Dispose() + { + DisposeInternal(true); + } + #endregion + + } +} diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs index 9c2534c14..a5c129c0a 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text; using System.Threading; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Net; using System.Globalization; @@ -13,7 +12,7 @@ namespace PubnubApi.EndPoint { - public class SubscribeOperation : PubnubCoreBase + public class SubscribeOperation : PubnubCoreBase, ISubscribeOperation { private readonly PNConfiguration config; private readonly IJsonPluggableLibrary jsonLibrary; @@ -37,9 +36,78 @@ public SubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary js pubnubLog = log; pubnubTelemetryMgr = telemetryManager; pubnubTokenMgr = tokenManager; + + PubnubInstance = instance; + if (!MultiChannelSubscribe.ContainsKey(instance.InstanceId)) + { + MultiChannelSubscribe.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!MultiChannelGroupSubscribe.ContainsKey(instance.InstanceId)) + { + MultiChannelGroupSubscribe.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelRequest.ContainsKey(instance.InstanceId)) + { + ChannelRequest.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelInternetStatus.ContainsKey(instance.InstanceId)) + { + ChannelInternetStatus.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelGroupInternetStatus.ContainsKey(instance.InstanceId)) + { + ChannelGroupInternetStatus.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelLocalUserState.ContainsKey(instance.InstanceId)) + { + ChannelLocalUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); + } + if (!ChannelGroupLocalUserState.ContainsKey(instance.InstanceId)) + { + ChannelGroupLocalUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); + } + if (!ChannelUserState.ContainsKey(instance.InstanceId)) + { + ChannelUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); + } + if (!ChannelGroupUserState.ContainsKey(instance.InstanceId)) + { + ChannelGroupUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); + } + if (!ChannelReconnectTimer.ContainsKey(instance.InstanceId)) + { + ChannelReconnectTimer.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelGroupReconnectTimer.ContainsKey(instance.InstanceId)) + { + ChannelGroupReconnectTimer.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); + } + if (!SubscribeDisconnected.ContainsKey(instance.InstanceId)) + { + SubscribeDisconnected.GetOrAdd(instance.InstanceId, false); + } + if (!LastSubscribeTimetoken.ContainsKey(instance.InstanceId)) + { + LastSubscribeTimetoken.GetOrAdd(instance.InstanceId, 0); + } + if (!LastSubscribeRegion.ContainsKey(instance.InstanceId)) + { + LastSubscribeRegion.GetOrAdd(instance.InstanceId, 0); + } + if (!SubscribeRequestTracker.ContainsKey(instance.InstanceId)) + { + SubscribeRequestTracker.GetOrAdd(instance.InstanceId, DateTime.Now); + } + } - public SubscribeOperation Channels(string[] channels) + public List SubscribeListenerList + { + get; + set; + } = new List(); + + public ISubscribeOperation Channels(string[] channels) { if (channels != null && channels.Length > 0 && !string.IsNullOrEmpty(channels[0])) { @@ -48,7 +116,7 @@ public SubscribeOperation Channels(string[] channels) return this; } - public SubscribeOperation ChannelGroups(string[] channelGroups) + public ISubscribeOperation ChannelGroups(string[] channelGroups) { if (channelGroups != null && channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0])) { @@ -57,19 +125,19 @@ public SubscribeOperation ChannelGroups(string[] channelGroups) return this; } - public SubscribeOperation WithTimetoken(long timetoken) + public ISubscribeOperation WithTimetoken(long timetoken) { this.subscribeTimetoken = timetoken; return this; } - public SubscribeOperation WithPresence() + public ISubscribeOperation WithPresence() { this.presenceSubscribeEnabled = true; return this; } - public SubscribeOperation QueryParam(Dictionary customQueryParam) + public ISubscribeOperation QueryParam(Dictionary customQueryParam) { this.queryParam = customQueryParam; return this; @@ -191,69 +259,5 @@ internal bool Retry(bool reconnect, bool resetSubscribeTimetoken) } } - internal void CurrentPubnubInstance(Pubnub instance) - { - PubnubInstance = instance; - if (!MultiChannelSubscribe.ContainsKey(instance.InstanceId)) - { - MultiChannelSubscribe.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!MultiChannelGroupSubscribe.ContainsKey(instance.InstanceId)) - { - MultiChannelGroupSubscribe.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!ChannelRequest.ContainsKey(instance.InstanceId)) - { - ChannelRequest.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!ChannelInternetStatus.ContainsKey(instance.InstanceId)) - { - ChannelInternetStatus.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!ChannelGroupInternetStatus.ContainsKey(instance.InstanceId)) - { - ChannelGroupInternetStatus.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!ChannelLocalUserState.ContainsKey(instance.InstanceId)) - { - ChannelLocalUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); - } - if (!ChannelGroupLocalUserState.ContainsKey(instance.InstanceId)) - { - ChannelGroupLocalUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); - } - if (!ChannelUserState.ContainsKey(instance.InstanceId)) - { - ChannelUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); - } - if (!ChannelGroupUserState.ContainsKey(instance.InstanceId)) - { - ChannelGroupUserState.GetOrAdd(instance.InstanceId, new ConcurrentDictionary>()); - } - if (!ChannelReconnectTimer.ContainsKey(instance.InstanceId)) - { - ChannelReconnectTimer.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!ChannelGroupReconnectTimer.ContainsKey(instance.InstanceId)) - { - ChannelGroupReconnectTimer.GetOrAdd(instance.InstanceId, new ConcurrentDictionary()); - } - if (!SubscribeDisconnected.ContainsKey(instance.InstanceId)) - { - SubscribeDisconnected.GetOrAdd(instance.InstanceId, false); - } - if (!LastSubscribeTimetoken.ContainsKey(instance.InstanceId)) - { - LastSubscribeTimetoken.GetOrAdd(instance.InstanceId, 0); - } - if (!LastSubscribeRegion.ContainsKey(instance.InstanceId)) - { - LastSubscribeRegion.GetOrAdd(instance.InstanceId, 0); - } - if (!SubscribeRequestTracker.ContainsKey(instance.InstanceId)) - { - SubscribeRequestTracker.GetOrAdd(instance.InstanceId, DateTime.Now); - } - } } } diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs new file mode 100644 index 000000000..2a4f7282f --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -0,0 +1,769 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Globalization; +using PubnubApi.PubnubEventEngine; +using PubnubApi.EventEngine.Subscribe; +using PubnubApi; + +namespace PubnubApi.EndPoint +{ + public class SubscribeOperation2: ISubscribeOperation + { + private readonly PNConfiguration config; + private readonly IJsonPluggableLibrary jsonLibrary; + private readonly IPubnubUnitTest unit; + private readonly IPubnubLog pubnubLog; + private readonly EndPoint.TelemetryManager pubnubTelemetryMgr; + private readonly EndPoint.TokenManager pubnubTokenMgr; + + private List subscribeChannelNames = new List(); + private List subscribeChannelGroupNames = new List(); + private long subscribeTimetoken = -1; + private bool presenceSubscribeEnabled; + private SubscribeManager2 manager; + private Dictionary queryParam; + private Pubnub PubnubInstance; + private SubscribeEventEngine subscribeEventEngine; + public SubscribeEventEngineFactory subscribeEventEngineFactory; + public string instanceId; + public List SubscribeListenerList + { + get; + set; + } = new List(); + + public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager,SubscribeEventEngineFactory subscribeEventEngineFactory, string instanceId, Pubnub instance) + { + PubnubInstance = instance; + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + pubnubTokenMgr = tokenManager; + this.subscribeEventEngineFactory = subscribeEventEngineFactory; + this.instanceId = instanceId; + + var eventEmitter = new EventEmitter(); + eventEmitter.RegisterJsonListener(JsonCallback); + + // pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); + // pnEventEngine.PubnubUnitTest = unit; + // pnEventEngine.Setup(config); + + // if (pnEventEngine.PubnubUnitTest != null) + // { + // pnEventEngine.PubnubUnitTest.EventTypeList = new List>(); + // } + // else + // { + //pnEventEngine.InitialState(new State(StateType.Unsubscribed) { EventType = EventType.SubscriptionChanged }); + // } + } + + private void HandshakeEffect_CancelHandshakeRequested(object sender, CancelHandshakeRequestEventArgs e) + { + manager.HandshakeRequestCancellation(); + } + private void HandshakeReconnectEffect_CancelHandshakeRequested(object sender, CancelHandshakeReconnectRequestEventArgs e) + { + manager.HandshakeRequestCancellation(); + } + private void ReceivingEffect_CancelReceiveRequested(object sender, CancelReceiveRequestEventArgs e) + { + manager.ReceiveRequestCancellation(); + } + private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, CancelReceiveReconnectRequestEventArgs e) + { + manager.ReceiveReconnectRequestCancellation(); + } + + private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) + { + } + + protected void ProcessListenerCallback(List result, bool zeroTimeTokenRequest, int messageCount, string[] channels, string[] channelGroups) + { + bool callbackAvailable = false; + if (result != null && result.Count >= 1 && SubscribeListenerList.Count >= 1) + { + callbackAvailable = true; + } + if (callbackAvailable) + { + if (zeroTimeTokenRequest) + { + ResponseToConnectCallback(PNOperationType.PNSubscribeOperation, channels, channelGroups); + } + else if (messageCount > 0) + { + ResponseToUserCallback(result, PNOperationType.PNSubscribeOperation); + } + } + } + + private void ResponseToConnectCallback(PNOperationType type, string[] channels, string[] channelGroups) + { + StatusBuilder statusBuilder = new StatusBuilder(config, jsonLibrary); + PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNConnectedCategory, null, (int)HttpStatusCode.OK, null); + + Announce(status); + } + + internal void Announce(PNStatus status) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Status(PubnubInstance, status); + } + } + + internal void Announce(PNMessageResult message) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Message(PubnubInstance, message); + } + } + + internal void Announce(PNSignalResult message) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Signal(PubnubInstance, message); + } + } + + internal void Announce(PNFileEventResult message) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].File(PubnubInstance, message); + } + } + + internal void Announce(PNPresenceEventResult presence) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Presence(PubnubInstance, presence); + } + } + + internal void Announce(PNObjectEventResult objectApiEvent) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].ObjectEvent(PubnubInstance, objectApiEvent); + } + } + + internal void Announce(PNMessageActionEventResult messageActionEvent) + { + List callbackList = SubscribeListenerList; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].MessageAction(PubnubInstance, messageActionEvent); + } + } + + private void ResponseToUserCallback(List result, PNOperationType type) + { + IPubnubLog currentLog = null; + try + { + switch (type) + { + case PNOperationType.PNSubscribeOperation: + case PNOperationType.Presence: + List messageList = GetMessageFromMultiplexResult(result); + if (messageList != null && messageList.Count > 0) + { + if (messageList.Count >= config.RequestMessageCountThreshold) + { + StatusBuilder statusBuilder = new StatusBuilder(config, jsonLibrary); + PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNRequestMessageCountExceededCategory, null, (int)HttpStatusCode.OK, null); + Announce(status); + } + + if (config != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList.Count = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageList.Count), config.LogVerbosity); + } + for (int messageIndex = 0; messageIndex < messageList.Count; messageIndex++) + { + SubscribeMessage currentMessage = messageList[messageIndex]; + if (currentMessage != null) + { + if (config != null && currentLog != null && config.DedupOnSubscribe && IsTargetForDedup(currentMessage)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList for loop - messageIndex = {1} => IsTargetForDedup", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageIndex), config.LogVerbosity); + continue; + } + + string currentMessageChannel = currentMessage.Channel; + string currentMessageChannelGroup = currentMessage.SubscriptionMatch; + + if (currentMessageChannel.Replace("-pnpres", "") == currentMessageChannelGroup.Replace("-pnpres", "")) + { + currentMessageChannelGroup = ""; + } + + object payload = currentMessage.Payload; + + List payloadContainer = new List(); //First item always message + if (currentMessageChannel.Contains("-pnpres") || currentMessageChannel.Contains(".*-pnpres")) + { + payloadContainer.Add(payload); + } + else if (currentMessage.MessageType == 2) //Objects Simplification events + { + double objectsVersion = -1; + Dictionary objectsDic = payload as Dictionary; + if (objectsDic != null + && objectsDic.ContainsKey("source") && objectsDic.ContainsKey("version") + && objectsDic["source"].ToString() == "objects" && Double.TryParse(objectsDic["version"].ToString(), out objectsVersion)) + { + if (objectsVersion.CompareTo(2D) == 0) //Process only version=2 for Objects Simplification. Ignore 1. + { + payloadContainer.Add(payload); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - Legacy Objects V1. Ignoring this.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + continue; + } + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - MessageType =2 but NOT valid format to process", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + continue; + } + } + else + { + if (config.CipherKey.Length > 0 && currentMessage.MessageType != 1) //decrypt the subscriber message if cipherkey is available + { + string decryptMessage = ""; + PubnubCrypto aes = new PubnubCrypto(config.CipherKey, config, currentLog, null); + try + { + decryptMessage = aes.Decrypt(payload.ToString()); + } + catch (Exception ex) + { + decryptMessage = "**DECRYPT ERROR**"; + + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(ex); + PNStatus status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, null, (int)HttpStatusCode.NotFound, new PNException(ex)); + if (!string.IsNullOrEmpty(currentMessageChannel)) + { + status.AffectedChannels.Add(currentMessageChannel); + status.AffectedChannels = status.AffectedChannels.Distinct().ToList(); + } + if (!string.IsNullOrEmpty(currentMessageChannelGroup)) + { + status.AffectedChannelGroups.Add(currentMessageChannelGroup); + status.AffectedChannelGroups = status.AffectedChannelGroups.Distinct().ToList(); + } + + Announce(status); + } + object decodeMessage = (decryptMessage == "**DECRYPT ERROR**") ? decryptMessage : jsonLibrary.DeserializeToObject(decryptMessage); + + payloadContainer.Add(decodeMessage); + } + else + { + string payloadJson = jsonLibrary.SerializeToJsonString(payload); + object payloadJObject = jsonLibrary.BuildJsonObject(payloadJson); + if (payloadJObject == null) + { + payloadContainer.Add(payload); + } + else + { + payloadContainer.Add(payloadJObject); + } + } + } + + object userMetaData = currentMessage.UserMetadata; + + payloadContainer.Add(userMetaData); //Second one always user meta data + + payloadContainer.Add(currentMessage.PublishTimetokenMetadata.Timetoken); //Third one always Timetoken + + payloadContainer.Add(currentMessage.IssuingClientId); //Fourth one always Publisher + + if (!string.IsNullOrEmpty(currentMessageChannelGroup)) //Add cg first before channel + { + payloadContainer.Add(currentMessageChannelGroup); + } + + if (!string.IsNullOrEmpty(currentMessageChannel)) + { + payloadContainer.Add(currentMessageChannel); + } + + if (currentMessage.MessageType == 1) + { + ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); + PNMessageResult pnMessageResult = responseBuilder.JsonToObject>(payloadContainer, true); + if (pnMessageResult != null) + { + PNSignalResult signalMessage = new PNSignalResult + { + Channel = pnMessageResult.Channel, + Message = pnMessageResult.Message, + Subscription = pnMessageResult.Subscription, + Timetoken = pnMessageResult.Timetoken, + UserMetadata = pnMessageResult.UserMetadata, + Publisher = pnMessageResult.Publisher + }; + Announce(signalMessage); + } + } + else if (currentMessage.MessageType == 2) + { + ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); + PNObjectEventResult objectApiEvent = responseBuilder.JsonToObject(payloadContainer, true); + if (objectApiEvent != null) + { + Announce(objectApiEvent); + } + } + else if (currentMessage.MessageType == 3) + { + ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); + PNMessageActionEventResult msgActionEventEvent = responseBuilder.JsonToObject(payloadContainer, true); + if (msgActionEventEvent != null) + { + Announce(msgActionEventEvent); + } + } + else if (currentMessage.MessageType == 4) + { + ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); + PNMessageResult pnFileResult = responseBuilder.JsonToObject>(payloadContainer, true); + if (pnFileResult != null) + { + PNFileEventResult fileMessage = new PNFileEventResult + { + Channel = pnFileResult.Channel, + Subscription = pnFileResult.Subscription, + Timetoken = pnFileResult.Timetoken, + Publisher = pnFileResult.Publisher, + }; + Dictionary pnMsgObjDic = JsonDataParseInternalUtil.ConvertToDictionaryObject(pnFileResult.Message); + if (pnMsgObjDic != null && pnMsgObjDic.Count > 0) + { + if (pnMsgObjDic.ContainsKey("message") && pnMsgObjDic["message"] != null) + { + fileMessage.Message = pnMsgObjDic["message"]; + } + if (pnMsgObjDic.ContainsKey("file")) + { + Dictionary fileObjDic = JsonDataParseInternalUtil.ConvertToDictionaryObject(pnMsgObjDic["file"]); + if (fileObjDic != null && fileObjDic.ContainsKey("id") && fileObjDic.ContainsKey("name")) + { + fileMessage.File = new PNFile { Id = fileObjDic["id"].ToString(), Name = fileObjDic["name"].ToString() }; + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, currentLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance.InstanceId); + Uri fileUrlRequest = urlBuilder.BuildGetFileUrlOrDeleteReqest("GET", "", fileMessage.Channel, fileMessage.File.Id, fileMessage.File.Name, null, type); + fileMessage.File.Url = fileUrlRequest.ToString(); + } + } + } + else + { + if (pnFileResult.Message != null) + { + fileMessage.Message = pnFileResult.Message; + } + } + Announce(fileMessage); + } + } + else if (currentMessageChannel.Contains("-pnpres")) + { + ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); + PNPresenceEventResult presenceEvent = responseBuilder.JsonToObject(payloadContainer, true); + if (presenceEvent != null) + { + Announce(presenceEvent); + } + } + else + { + if (config != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - payload = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLibrary.SerializeToJsonString(payloadContainer)), config.LogVerbosity); + } + ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); + PNMessageResult userMessage = responseBuilder.JsonToObject>(payloadContainer, true); + if (userMessage != null) + { + Announce(userMessage); + } + } + + } + else + { + if (config != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList for loop - messageIndex = {1} => null message", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageIndex), config.LogVerbosity); + } + } + } + + } + break; + case PNOperationType.PNHeartbeatOperation: + break; + default: + break; + } + } + catch (Exception ex) + { + if (config != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - Exception = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), config.LogVerbosity); + } + } + } + + private bool IsTargetForDedup(SubscribeMessage message) + { + bool isTargetOfDedup = false; + PNConfiguration currentConfig; + IPubnubLog currentLog; + try + { + //if (pubnubSubscribeDuplicationManager.IsDuplicate(message)) + //{ + // isTargetOfDedup = true; + // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + // { + // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, Dedupe - Duplicate skipped - msg = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLib.SerializeToJsonString(message)), currentConfig.LogVerbosity); + // } + //} + //else + //{ + // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + // { + // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, Dedupe - AddEntry - msg = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLib.SerializeToJsonString(message)), currentConfig.LogVerbosity); + // } + // pubnubSubscribeDuplicationManager.AddEntry(message); + //} + } + catch (Exception ex) + { + //Log and ignore any exception due to Dedupe manager + //if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + //{ + // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, IsTargetForDedup - dedupe error = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), currentConfig.LogVerbosity); + //} + } + + return isTargetOfDedup; + } + + private List GetMessageFromMultiplexResult(List result) + { + List jsonMessageList = null; + List msgList = new List(); + + Dictionary messageDicObj = jsonLibrary.ConvertToDictionaryObject(result[1]); + if (messageDicObj != null && messageDicObj.Count > 0 && messageDicObj.ContainsKey("m")) + { + jsonMessageList = messageDicObj["m"] as List; + } + else + { + messageDicObj = jsonLibrary.ConvertToDictionaryObject(result[0]); + if (messageDicObj != null && messageDicObj.Count > 0 && messageDicObj.ContainsKey("m")) + { + jsonMessageList = messageDicObj["m"] as List; + } + } + + if (jsonMessageList != null && jsonMessageList.Count > 0) + { + foreach (Dictionary dicItem in jsonMessageList) + { + if (dicItem.Count > 0) + { + SubscribeMessage msg = new SubscribeMessage(); + foreach (string key in dicItem.Keys) + { + switch (key.ToLowerInvariant()) + { + case "a": + msg.Shard = dicItem[key].ToString(); + break; + case "b": + msg.SubscriptionMatch = dicItem[key].ToString(); + break; + case "c": + msg.Channel = dicItem[key].ToString(); + break; + case "d": + msg.Payload = dicItem[key]; + break; + case "e": + int subscriptionTypeIndicator; + var _ = Int32.TryParse(dicItem[key].ToString(), out subscriptionTypeIndicator); + msg.MessageType = subscriptionTypeIndicator; + break; + case "f": + msg.Flags = dicItem[key].ToString(); + break; + case "i": + msg.IssuingClientId = dicItem[key].ToString(); + break; + case "k": + msg.SubscribeKey = dicItem[key].ToString(); + break; + case "s": + int seqNum; + _ = Int32.TryParse(dicItem[key].ToString(), out seqNum); + msg.SequenceNumber = seqNum; + break; + case "o": + Dictionary ttOriginMetaData = jsonLibrary.ConvertToDictionaryObject(dicItem[key]); + if (ttOriginMetaData != null && ttOriginMetaData.Count > 0) + { + TimetokenMetadata ttMeta = new TimetokenMetadata(); + + foreach (string metaKey in ttOriginMetaData.Keys) + { + if (metaKey.ToLowerInvariant().Equals("t", StringComparison.OrdinalIgnoreCase)) + { + long timetoken; + _ = Int64.TryParse(ttOriginMetaData[metaKey].ToString(), out timetoken); + ttMeta.Timetoken = timetoken; + } + else if (metaKey.ToLowerInvariant().Equals("r", StringComparison.OrdinalIgnoreCase)) + { + ttMeta.Region = ttOriginMetaData[metaKey].ToString(); + } + } + msg.OriginatingTimetoken = ttMeta; + } + break; + case "p": + Dictionary ttPublishMetaData = jsonLibrary.ConvertToDictionaryObject(dicItem[key]); + if (ttPublishMetaData != null && ttPublishMetaData.Count > 0) + { + TimetokenMetadata ttMeta = new TimetokenMetadata(); + + foreach (string metaKey in ttPublishMetaData.Keys) + { + string currentMetaKey = metaKey.ToLowerInvariant(); + + if (currentMetaKey.Equals("t", StringComparison.OrdinalIgnoreCase)) + { + long timetoken; + _ = Int64.TryParse(ttPublishMetaData[metaKey].ToString(), out timetoken); + ttMeta.Timetoken = timetoken; + } + else if (currentMetaKey.Equals("r", StringComparison.OrdinalIgnoreCase)) + { + ttMeta.Region = ttPublishMetaData[metaKey].ToString(); + } + } + msg.PublishTimetokenMetadata = ttMeta; + } + break; + case "u": + msg.UserMetadata = dicItem[key]; + break; + default: + break; + } + } + + msgList.Add(msg); + } + } + } + return msgList; + } + + private void LogCallback(string log) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), log), config.LogVerbosity); + } + + public ISubscribeOperation Channels(string[] channels) + { + if (channels != null && channels.Length > 0 && !string.IsNullOrEmpty(channels[0])) + { + this.subscribeChannelNames.AddRange(channels); + } + return this; + } + + public ISubscribeOperation ChannelGroups(string[] channelGroups) + { + if (channelGroups != null && channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0])) + { + this.subscribeChannelGroupNames.AddRange(channelGroups); + } + return this; + } + + public ISubscribeOperation WithTimetoken(long timetoken) + { + this.subscribeTimetoken = timetoken; + return this; + } + + public ISubscribeOperation WithPresence() + { + this.presenceSubscribeEnabled = true; + return this; + } + + public ISubscribeOperation QueryParam(Dictionary customQueryParam) + { + this.queryParam = customQueryParam; + return this; + } + + public void Execute() + { + if (this.subscribeChannelNames == null) + { + this.subscribeChannelNames = new List(); + } + + if (this.subscribeChannelGroupNames == null) + { + this.subscribeChannelGroupNames = new List(); + } + + if (this.presenceSubscribeEnabled) + { + List presenceChannelNames = (this.subscribeChannelNames != null && this.subscribeChannelNames.Count > 0 && !string.IsNullOrEmpty(this.subscribeChannelNames[0])) + ? this.subscribeChannelNames.Select(c => string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", c)).ToList() : new List(); + List presenceChannelGroupNames = (this.subscribeChannelGroupNames != null && this.subscribeChannelGroupNames.Count > 0 && !string.IsNullOrEmpty(this.subscribeChannelGroupNames[0])) + ? this.subscribeChannelGroupNames.Select(c => string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", c)).ToList() : new List(); + + if (this.subscribeChannelNames != null && presenceChannelNames.Count > 0) + { + this.subscribeChannelNames.AddRange(presenceChannelNames); + } + + if (this.subscribeChannelGroupNames != null && presenceChannelGroupNames.Count > 0) + { + this.subscribeChannelGroupNames.AddRange(presenceChannelGroupNames); + } + } + + string[] channelNames = this.subscribeChannelNames != null ? this.subscribeChannelNames.ToArray() : null; + string[] channelGroupNames = this.subscribeChannelGroupNames != null ? this.subscribeChannelGroupNames.ToArray() : null; + + Subscribe(channelNames, channelGroupNames, this.queryParam); + } + + private void Subscribe(string[] channels, string[] channelGroups, Dictionary externalQueryParam) + { + Action statusListener = null; + Action> messageListener = null; + if ((channels == null || channels.Length == 0) && (channelGroups == null || channelGroups.Length == 0)) + { + throw new ArgumentException("Either Channel Or Channel Group or Both should be provided."); + } + + if (this.subscribeEventEngineFactory.hasEventEngine(instanceId)) + { + subscribeEventEngine = subscribeEventEngineFactory.getEventEngine(instanceId); + } + else + { + if (SubscribeListenerList != null && SubscribeListenerList.Count > 0) { + messageListener = MessageEmitter; + statusListener = StatusEmitter; + } + var subscribeManager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); + subscribeEventEngine = subscribeEventEngineFactory.initializeEventEngine(instanceId, PubnubInstance, config, subscribeManager, statusListener, messageListener); + + } + subscribeEventEngine.Subscribe(channels, channelGroups); + } + + internal bool Retry(bool reconnect) + { + return false; + //if (manager == null) + //{ + // return false; + //} + + //if (reconnect) + //{ + // return manager.Reconnect(false); + //} + //else + //{ + // return manager.Disconnect(); + //} + } + + internal bool Retry(bool reconnect, bool resetSubscribeTimetoken) + { + return false; + //if (manager == null) + //{ + // return false; + //} + + //if (reconnect) + //{ + // return manager.Reconnect(resetSubscribeTimetoken); + //} + //else + //{ + // return manager.Disconnect(); + //} + } + + internal void CurrentPubnubInstance(Pubnub instance) + { + PubnubInstance = instance; + } + private void MessageEmitter(Pubnub pubnubInstance, PNMessageResult messageResult) + { + foreach (var listener in SubscribeListenerList) { + listener.Message(pubnubInstance, messageResult); + } + } + + private void StatusEmitter(Pubnub pubnubInstance, PNStatus status) + { + for(int i=0; i < SubscribeListenerList.Count; i++) + { + SubscribeCallback listener = SubscribeListenerList[i]; + if (listener != null) + { + listener.Status(pubnubInstance, status); + } + } + } + + } +} diff --git a/src/Api/PubnubApi/EndPoint/Push/AddPushChannelOperation.cs b/src/Api/PubnubApi/EndPoint/Push/AddPushChannelOperation.cs index 8304fcee6..3c3f2a2c8 100644 --- a/src/Api/PubnubApi/EndPoint/Push/AddPushChannelOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Push/AddPushChannelOperation.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using PubnubApi.Interface; using System.Net; using System.Threading.Tasks; using System.Threading; diff --git a/src/Api/PubnubApi/EndPoint/Push/AuditPushChannelOperation.cs b/src/Api/PubnubApi/EndPoint/Push/AuditPushChannelOperation.cs index a510cc9b7..4b23a95cf 100644 --- a/src/Api/PubnubApi/EndPoint/Push/AuditPushChannelOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Push/AuditPushChannelOperation.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Push/RemoveAllPushChannelsOperation.cs b/src/Api/PubnubApi/EndPoint/Push/RemoveAllPushChannelsOperation.cs index 2fa4b8648..dd90ab6ee 100644 --- a/src/Api/PubnubApi/EndPoint/Push/RemoveAllPushChannelsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Push/RemoveAllPushChannelsOperation.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/Push/RemovePushChannelOperation.cs b/src/Api/PubnubApi/EndPoint/Push/RemovePushChannelOperation.cs index e997bc4c0..a95e65653 100644 --- a/src/Api/PubnubApi/EndPoint/Push/RemovePushChannelOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Push/RemovePushChannelOperation.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/StoragePlayback/DeleteMessageOperation.cs b/src/Api/PubnubApi/EndPoint/StoragePlayback/DeleteMessageOperation.cs index 3319cef23..411892f2f 100644 --- a/src/Api/PubnubApi/EndPoint/StoragePlayback/DeleteMessageOperation.cs +++ b/src/Api/PubnubApi/EndPoint/StoragePlayback/DeleteMessageOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/StoragePlayback/FetchHistoryOperation.cs b/src/Api/PubnubApi/EndPoint/StoragePlayback/FetchHistoryOperation.cs index 99d4a45f3..6a7f9c072 100644 --- a/src/Api/PubnubApi/EndPoint/StoragePlayback/FetchHistoryOperation.cs +++ b/src/Api/PubnubApi/EndPoint/StoragePlayback/FetchHistoryOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/StoragePlayback/HistoryOperation.cs b/src/Api/PubnubApi/EndPoint/StoragePlayback/HistoryOperation.cs index 69b1bfcb2..871d56f71 100644 --- a/src/Api/PubnubApi/EndPoint/StoragePlayback/HistoryOperation.cs +++ b/src/Api/PubnubApi/EndPoint/StoragePlayback/HistoryOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/StoragePlayback/MessageCountsOperation.cs b/src/Api/PubnubApi/EndPoint/StoragePlayback/MessageCountsOperation.cs index 706ae92af..a765608a3 100644 --- a/src/Api/PubnubApi/EndPoint/StoragePlayback/MessageCountsOperation.cs +++ b/src/Api/PubnubApi/EndPoint/StoragePlayback/MessageCountsOperation.cs @@ -1,5 +1,4 @@ -using PubnubApi.Interface; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/EndPoint/TimeOperation.cs b/src/Api/PubnubApi/EndPoint/TimeOperation.cs index c4605b60a..a2a9f5dea 100644 --- a/src/Api/PubnubApi/EndPoint/TimeOperation.cs +++ b/src/Api/PubnubApi/EndPoint/TimeOperation.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using PubnubApi.Interface; using System.Threading.Tasks; using System.Threading; using System.Net; diff --git a/src/Api/PubnubApi/EventEngine/Common/Delay.cs b/src/Api/PubnubApi/EventEngine/Common/Delay.cs new file mode 100644 index 000000000..3afb9a4a6 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Common/Delay.cs @@ -0,0 +1,60 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace PubnubApi.EventEngine.Common +{ + public class Delay + { + public bool Cancelled { get; private set; } = false; + private readonly TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); + private readonly object monitor = new object(); + private readonly int milliseconds; + + public Delay(int milliseconds) + { + this.milliseconds = milliseconds; + } + + public Task Start() + { + #if NETFX_CORE || WINDOWS_UWP || UAP || NETSTANDARD10 || NETSTANDARD11 || NETSTANDARD12 + Task taskAwaiter = Task.Factory.StartNew(AwaiterLoop); + taskAwaiter.Wait(); + #else + Thread awaiterThread = new Thread(AwaiterLoop); + awaiterThread.Start(); + #endif + return taskCompletionSource.Task; } + + public void Cancel() + { + lock (monitor) + { + Cancelled = true; + Monitor.Pulse(monitor); + } + } + + private void AwaiterLoop() + { + while(true) + { + lock (monitor) + { + if (Cancelled) + { + taskCompletionSource.SetCanceled(); + break; + } + Monitor.Wait(monitor, milliseconds); + if (Cancelled) + { + taskCompletionSource.SetCanceled(); + break; + } + taskCompletionSource.SetResult(null); + } + } + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs new file mode 100644 index 000000000..e4fea80fc --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace PubnubApi.EventEngine.Core { + public class EffectDispatcher { + // assumes 1 instance of handler - capable of managing itself + private readonly Dictionary effectInvocationHandlerMap = + new Dictionary(); + + public event System.Action OnEffectDispatch; + + /// + /// Dispatch an invocation i.e. call a registered effect handler. + /// + public async Task Dispatch(IEffectInvocation invocation) { + if (!effectInvocationHandlerMap.ContainsKey(invocation.GetType())) { + throw new ArgumentException($"No handler for {invocation.GetType().Name} found."); + } + + OnEffectDispatch?.Invoke(invocation); + + if (invocation is IEffectCancelInvocation) { + await effectInvocationHandlerMap[invocation.GetType()].Cancel(); + } else + { + var handler = effectInvocationHandlerMap[invocation.GetType()]; + if (handler.IsBackground(invocation)) + await handler.Run(invocation); + else + await handler.Run(invocation); + } + } + + /// + /// Assign a handler implementation to an invocation. + /// + public EffectDispatcher Register(TEffectHandler handler) + where TEffectInvocation : IEffectInvocation + where TEffectHandler : IEffectHandler { + // TODO log + // if (effectInvocationHandlerMap.ContainsKey(typeof(TEffectInvocation))) + + effectInvocationHandlerMap[typeof(TEffectInvocation)] = handler; + return this; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs new file mode 100644 index 000000000..48ca50f08 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -0,0 +1,77 @@ +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace PubnubApi.EventEngine.Core { + public abstract class Engine { + public EventQueue EventQueue = new EventQueue(); + + protected EffectDispatcher dispatcher = new EffectDispatcher(); + protected State currentState; + + private Task currentTransitionLoop = Utils.EmptyTask; + + private readonly IEffectInvocation[] emptyInvocationList = new IEffectInvocation[0]; + + /// + /// Subscribe to receive notification on effect dispatch + /// + public event System.Action OnEffectDispatch + { + add => dispatcher.OnEffectDispatch += value; + remove => dispatcher.OnEffectDispatch -= value; + } + + /// + /// Subscribe to receive notification on state transition + /// + public event System.Action OnStateTransition; + + /// + /// Subscribe to receive notification on event being queued + /// + public event System.Action OnEventQueued; + + public Engine() { + EventQueue.OnEventQueued += OnEvent; + } + + ~Engine() { + EventQueue.OnEventQueued -= OnEvent; + } + + private async void OnEvent(EventQueue q) + { + OnEventQueued?.Invoke(q.Peek()); + await currentTransitionLoop; + currentTransitionLoop = EventQueue.Loop(async e => currentState = await Transition(e)); + } + + private async Task Transition(IEvent e) { + var stateInvocationPair = currentState.Transition(e); + OnStateTransition?.Invoke(stateInvocationPair); + + if (stateInvocationPair is null) { + return currentState; + } + + await ExecuteStateChange(currentState, stateInvocationPair.State, stateInvocationPair.Invocations); + + return stateInvocationPair.State; + } + + /// + /// Launch the invocations associated with transitioning between states + /// + private async Task ExecuteStateChange(State sourceState, State targetState, IEnumerable invocations) { + foreach (var effectInvocation in sourceState.OnExit ?? emptyInvocationList) { + await dispatcher.Dispatch(effectInvocation); + } + foreach (var effectInvocation in invocations ?? emptyInvocationList) { + await dispatcher.Dispatch(effectInvocation); + } + foreach (var effectInvocation in targetState.OnEntry ?? emptyInvocationList) { + await dispatcher.Dispatch(effectInvocation); + } + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs new file mode 100644 index 000000000..c58119d7a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -0,0 +1,165 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace PubnubApi.EventEngine.Core +{ + /// + /// Generic effect handler. + /// + public interface IEffectHandler + { + Task Cancel(); + Task Run(IEffectInvocation invocation); + bool IsBackground(IEffectInvocation invocation); + } + + /// + /// Handler (implementation) for a given invocation. The invocation represents the input arguments of a handler. + /// + /// Associated invocation + public interface IEffectHandler : IEffectHandler where T : IEffectInvocation + { + Task Run(T invocation); + bool IsBackground(T invocation); + } + + public abstract class EffectHandler : IEffectHandler + where T : class, IEffectInvocation + { + public abstract Task Cancel(); + + public Task Run(IEffectInvocation invocation) => Run(invocation as T); + + public bool IsBackground(IEffectInvocation invocation) => IsBackground(invocation as T); + + public abstract Task Run(T invocation); + + public abstract bool IsBackground(T invocation); + } + + /// + /// Implement a handler a cancellable invocation. + /// + /// Connect type invocation + /// Cancel running invocation + public abstract class EffectCancellableHandler : EffectHandler, IEffectHandler + where T1 : class, IEffectInvocation + where T2 : class, IEffectCancelInvocation + { + // run is not implemented in cancel. + public Task Run(T2 invocation) + { + throw new NotImplementedException(); + } + + public bool IsBackground(T2 invocation) => false; + } + + + /// + /// Implement a handler for two invocations (meant for connect-reconnect pairs). Use EffectDoubleCancellableHandler to implement cancellable handler. + /// + /// Run type invocation + /// Retry type invocation + public abstract class EffectDoubleHandler : EffectHandler, IEffectHandler + where T1 : class, IEffectInvocation + where T2 : class, IEffectInvocation + { + + public new Task Run(IEffectInvocation invocation) => + invocation is T1 ? (this as EffectHandler).Run(invocation) : Run(invocation as T2); + + public new bool IsBackground(IEffectInvocation invocation) => + invocation is T1 ? (this as EffectHandler).IsBackground(invocation) : IsBackground(invocation as T2); + + public abstract Task Run(T2 invocation); + + public abstract bool IsBackground(T2 invocation); + } + + + /// + /// Implement a handler for two invocations (meant for connect-reconnect pairs) with a cancel invocation + /// + /// Run type invocation + /// Retry type invocation + /// Cancel connecting invocation + public abstract class EffectDoubleCancellableHandler : EffectDoubleHandler, IEffectHandler + where T1 : class, IEffectInvocation + where T2 : class, IEffectInvocation + where T3 : class, IEffectCancelInvocation + { + // Run is not implemented in cancel. + public Task Run(T3 invocation) + { + throw new NotImplementedException(); + } + + public bool IsBackground(T3 invocation) => false; + } + + /// + /// Implement a handler for two invocations (meant for connect-reconnect pairs) with a cancel invocation + /// + /// Run type invocation + /// Retry type invocation + /// Cancel run invocation + /// Cancel retry invocation + public abstract class EffectDoubleCancellableHandler : EffectDoubleCancellableHandler, + IEffectHandler + where T1 : class, IEffectInvocation + where T2 : class, IEffectInvocation + where T3 : class, IEffectCancelInvocation + where T4 : class, IEffectCancelInvocation + { + public Task Run(T4 invocation) + { + throw new NotImplementedException(); + } + + public bool IsBackground(T4 invocation) => false; + } + + + /// + /// An effect invocation. It represents calling Run() on a registered effect handler - calling it is orchestrated by the dispatcher. + /// + public interface IEffectInvocation + { + } + + /// + /// A cancel effect invocation. It represents calling Cancel() on a registered effect handler - calling it is orchestrated by the dispatcher. + /// + public interface IEffectCancelInvocation : IEffectInvocation + { + } + + public interface IEvent + { + }; + + public abstract class State + { + public virtual IEnumerable OnEntry { get; } = null; + public virtual IEnumerable OnExit { get; } = null; + + /// + /// The EE transition pure function. + /// + /// Input event + /// Target state and invocation list, or null for no-transition + public abstract TransitionResult Transition(IEvent e); + + public TransitionResult With(params IEffectInvocation[] invocations) + { + return new TransitionResult(this, invocations); + } + + public static implicit operator TransitionResult(State s) + { + return new TransitionResult(s); + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs new file mode 100644 index 000000000..e22991e0b --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace PubnubApi.EventEngine.Core +{ + public class EventQueue + { + private volatile Queue eventQueue = new Queue(); + private object lockObj = new object(); + + public event System.Action OnEventQueued; + + /// + /// Enqueue (fire) an event to the Event Engine. Handling that event is covered by the Engine itself. + /// + /// Event to be fired + public void Enqueue(IEvent e) + { + lock (lockObj) + { + // TODO de-dupe? Throttle? + eventQueue.Enqueue(e); + OnEventQueued?.Invoke(this); + } + } + + private IEvent Dequeue() + { + lock (lockObj) + { + return eventQueue.Any() ? eventQueue.Dequeue() : null; + } + } + + public IEvent Peek() + { + lock (lockObj) + { + return eventQueue.Peek(); + } + } + + public async Task Loop(System.Func> function) + { + while (Count > 0) + { + await function(Dequeue()); + } + } + + public int Count + { + get + { + lock (lockObj) + { + return eventQueue.Count; + } + } + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Core/Utils.cs b/src/Api/PubnubApi/EventEngine/Core/Utils.cs new file mode 100644 index 000000000..6436a262a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reflection; + +namespace PubnubApi.EventEngine.Core +{ + internal static class Utils + { + static Utils() + { + EmptyTask.Start(); + } + + internal static Task EmptyTask { get; } = new Task(() => null); + + internal static IEffectInvocation[] AsArray(this IEffectInvocation invocation) + { + return new IEffectInvocation[] { invocation }; + } + + internal static bool IsBackground(this IEffectHandler handler, IEffectInvocation invocation) + { + return (bool)handler.GetType() + .GetMethod("IsBackground") + .Invoke(handler, new object[] { invocation }); + } + + internal static Task Run(this IEffectHandler handler, IEffectInvocation invocation) + { + return (Task)handler.GetType() + .GetMethod("Run") + .Invoke(handler, new object[] { invocation }); + } + } + + public class TransitionResult + { + public State State => tuple.Item1; + public IEnumerable Invocations => tuple.Item2; + + private readonly Tuple> tuple; + + /// + /// Create a state-invocation pair with empty invocations + /// + public TransitionResult(State state) + { + this.tuple = new Tuple>(state, new IEffectInvocation[0]); + } + + /// + /// Create a state-invocation pair + /// + public TransitionResult(State state, IEnumerable invocations) + { + this.tuple = new Tuple>(state, invocations); + } + + /// + /// Create a state-invocation pair + /// + public TransitionResult(State state, params IEffectInvocation[] invocations) : this(state, invocations as IEnumerable) { } + + public static implicit operator Tuple>(TransitionResult t) + { + return t.tuple; + } + + public static implicit operator TransitionResult(Tuple> t) + { + return new TransitionResult(t.Item1, t.Item2); + } + + public override int GetHashCode() + { + return tuple.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs new file mode 100644 index 000000000..0f94f3a99 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace PubnubApi.PubnubEventEngine +{ + public class EffectDispatcher + { + public IPubnubUnitTest PubnubUnitTest { get; set; } + + private Dictionary effectInvocationActionMap; + public EffectDispatcher() + { + effectInvocationActionMap = new Dictionary(); + } + + public async void dispatch(EventType eventType,List effectInvocations, ExtendedState stateContext) + { + if (effectInvocations == null || effectInvocations.Count == 0) { return; } + foreach (var invocation in effectInvocations) { + PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", invocation.Name)); + System.Diagnostics.Debug.WriteLine("Found effect " + invocation.Effectype); + IEffectInvocationHandler currentEffectInvocationhandler; + if (effectInvocationActionMap.TryGetValue(invocation.Effectype, out currentEffectInvocationhandler)) + { + if (invocation.IsManaged()) + { + await Task.Factory.StartNew(()=> currentEffectInvocationhandler?.Start(stateContext, eventType)).ConfigureAwait(false); + } + else if (invocation.IsCancelling()) + { + await Task.Factory.StartNew(()=> currentEffectInvocationhandler?.Cancel()).ConfigureAwait(false); + } + else + { + currentEffectInvocationhandler.Run(stateContext); + } + } + } + + } + + public void Register(EventType type, IEffectInvocationHandler handler) + { + if (effectInvocationActionMap.ContainsKey(type)) + { + throw new ArgumentException("EventType already exist"); + } + effectInvocationActionMap.Add(type, handler); + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/EventEmitter.cs b/src/Api/PubnubApi/EventEngine/EventEmitter.cs new file mode 100644 index 000000000..1022e3665 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/EventEmitter.cs @@ -0,0 +1,37 @@ +using System; + +namespace PubnubApi.PubnubEventEngine +{ + public class EventEmitter + { + private Action? handler; + private Action? jsonListener; + + public void RegisterHandler(Action eventHandler) + { + this.handler = eventHandler; + } + + public void RegisterJsonListener(Action listenerHandler) + { + this.jsonListener = listenerHandler; + } + + public void emit(Event e) + { + if (handler == null) + { + throw new MissingMemberException("eventHandler is missing"); + } + this.handler(e); + } + + public void emit(string json, bool zeroTimetokenRequest, int messageCount) + { + if (jsonListener != null) + { + jsonListener(json, zeroTimetokenRequest, messageCount); + } + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs new file mode 100644 index 000000000..5614914d9 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -0,0 +1,781 @@ +using Newtonsoft.Json; +using PubnubApi.EndPoint; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PubnubApi.PubnubEventEngine +{ + public class PubnubError : Exception + { + + } + + #region Event + public abstract class Event + { + public virtual string Name { get; set; } + public virtual EventType EventType { get; set; } + public virtual EventPayload EventPayload { get; set; } + public virtual int Attempts { get; set; } + + public Event() + { + EventPayload = new EventPayload(); + } + } + + public class SubscriptionChanged : Event + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + } + public class Disconnect : Event + { + + } + public class Reconnect : Event + { + + } + public class HandshakeSuccess : Event + { + public SubscriptionCursor SubscriptionCursor { get; set; } + } + public class SubscriptionRestored : Event + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + } + public class HandshakeFailure : Event + { + public PubnubError Reason { get; set; } + } + public class HandshakeReconnectGiveUp : Event + { + public PubnubError Reason { get; set;} + } + public class HandshakeReconnectSuccess : Event + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + } + public class HandshakeReconnectFailure : Event + { + public PubnubError Reason { get; set;} + } + public class ReceiveSuccess : Event + { + public List Messages { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + } + public class ReceiveFailure : Event + { + public PubnubError Reason { get; set;} + } + public class ReceiveReconnectFailure : Event + { + public PubnubError Reason { get; set;} + } + public class ReceiveReconnectGiveUp : Event + { + public PubnubError Reason { get; set;} + } + public class ReceiveReconnectSuccess : Event + { + public List Messages { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + } + public class Fail : Event + { + + } + public class Success : Event + { + + } + #endregion + public class EventPayload + { + public List? Channels { get; set; } + public List? ChannelGroups { get; set; } + public long? Timetoken { get; set; } + public int? Region { get; set; } + + public Exception? exception { get; set; } + } + + + public class SubscriptionCursor + { + public long? Timetoken { get; set; } + public int? Region { get; set; } + } + + #region EffectInvocation + public abstract class EffectInvocation + { + public virtual string Name { get; set; } + public virtual EventType Effectype { get; set; } + public virtual EffectInvocationType InvocationType { get; set; } + public virtual IEffectInvocationHandler Handler { get; set; } + public abstract bool IsManaged(); + public abstract bool IsCancelling(); + } + public class ReceiveMessages: EffectInvocation + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + + public override bool IsManaged() + { + return true; + } + public override bool IsCancelling() + { + return false; + } + + } + public class CancelReceiveMessages : EffectInvocation + { + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } + } + public class ReceiveReconnect: EffectInvocation + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + public int Attempts { get; set; } + public PubnubError Reason { get; set; } + public override bool IsManaged() + { + return true; + } + public override bool IsCancelling() + { + return false; + } + } + public class CancelReceiveReconnect : EffectInvocation + { + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } + } + public class Handshake : EffectInvocation + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public override bool IsManaged() + { + return true; + } + public override bool IsCancelling() + { + return false; + } + } + public class CancelHandshake : EffectInvocation + { + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } + } + public class HandshakeReconnect : EffectInvocation + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public SubscriptionCursor SubscriptionCursor { get; set; } + public int Attempts { get; set; } + public PubnubError Reason { get; set; } + public override bool IsManaged() + { + return true; + } + public override bool IsCancelling() + { + return false; + } + } + public class CancelHandshakeReconnect : EffectInvocation + { + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } + } + public class EmitStatus : EffectInvocation + { + private readonly PNStatusCategory statusCategory; + public Action AnnounceStatus { get; set; } + public EmitStatus() + { + } + public EmitStatus(PNStatusCategory status) + { + statusCategory = status; + } + + public void Announce() + { + if (Handler != null) + { + PNStatus status = Handler.GetPNStatus(); + if (AnnounceStatus != null && status != null) + { + if (Handler is ReceivingEffectHandler && status.StatusCode == 200) + { + //Ignore Announce for 200 + return; + } + else if (Handler is ReceiveReconnectingEffectHandler && status.StatusCode == 200) + { + //Ignore Announce for 200 + return; + } + System.Diagnostics.Debug.WriteLine($"Status Category = {status.Category} to be announced"); + AnnounceStatus(status); + } + } + } + + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return false; + } + } + public class EmitMessages : EffectInvocation + { + public Action LogCallback { get; set; } + //public SubscribeOperation2 SubscribeOperation { get; set; } + public Action> AnnounceMessage { get; set; } + public EmitMessages(List messages) + { + + } + public void Announce() + { + } + + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return false; + } + } + + public class HandshakeFailed : EffectInvocation + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public override bool IsManaged() + { + return true; + } + public override bool IsCancelling() + { + return false; + } + } + public class CancelHandshakeFailed : EffectInvocation + { + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } + } + #endregion + public enum EventType + { + SubscriptionChanged, + SubscriptionRestored, + Handshake, + CancelHandshake, + HandshakeSuccess, + ReceiveMessages, + CancelReceiveMessages, + ReceiveSuccess, + HandshakeFailure, + CancelHandshakeFailure, + ReceiveFailure, + ReceiveReconnect, + CancelReceiveReconnect, + ReceiveReconnectSuccess, + ReceiveReconnectFailure, + ReceiveReconnectGiveUp, + HandshakeReconnect, + CancelHandshakeReconnect, + HandshakeReconnectSuccess, + HandshakeReconnectFailure, + HandshakeReconnectGiveUp, + ReconnectionFailed, + Disconnect, + Reconnect + } + + public class ExtendedState + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public long? Timetoken { get; set; } + public int? Region { get; set; } + public int Attempts { get; set; } + + public ExtendedState() + { + Channels = new List(); + ChannelGroups = new List(); + Timetoken = 0; + Region = 0; + Attempts = 0; + } + + } + + public class EventEngine + { + private EventEngine pnEventEngine = null; + + public ExtendedState Context; + public State? CurrentState { get; set; } + public List States { get; set; } + + public EffectDispatcher Dispatcher; + + public EventEmitter Emitter; + public IPubnubUnitTest PubnubUnitTest { get; set; } + + public EventEngine(EffectDispatcher dispatcher, EventEmitter emitter) + { + if (PubnubUnitTest != null ) + { + PubnubUnitTest.EventTypeList?.Clear(); + } + this.Dispatcher = dispatcher; + States = new List(); + Context = new ExtendedState(); + this.Emitter = emitter; + emitter.RegisterHandler(this.Transition); + } + + public State CreateState(StateType type) + { + var newState = new State(type); + if (States.Find(s=> s.StateType == type) != null) + { + throw new InvalidOperationException($"StateType = {type} already exist."); + } + States.Add(newState); + return newState; + } + + public void Transition(Event e) + { + if (CurrentState != null) { + State findState = States.Find((s) => s.StateType == CurrentState.StateType); + StateType nextStateType; + if (findState != null && findState.transitions != null && findState.transitions.TryGetValue(e.EventType, out nextStateType)) + { + System.Diagnostics.Debug.WriteLine($"Current State = {CurrentState.StateType}; Transition = {e.EventType}"); + if (PubnubUnitTest != null ) + { + PubnubUnitTest.EventTypeList.Add(new KeyValuePair("event", e.Name)); + PubnubUnitTest.Attempts = e.Attempts; + } + if (findState != null) + { + if (findState.ExitList != null && findState.ExitList.Count > 0) + { + Dispatcher.dispatch(e.EventType, findState.ExitList, this.Context); + } + if (findState.EffectInvocationsList != null + && findState.EffectInvocationsList.ContainsKey(e.EventType) + && findState.EffectInvocationsList[e.EventType].Count > 0) + { + List effectInvocationList = findState.EffectInvocationsList[e.EventType]; + if (effectInvocationList != null && effectInvocationList.Count > 0) + { + Dispatcher.dispatch(e.EventType, effectInvocationList, this.Context); + } + } + + CurrentState = States.Find((s) => s.StateType == nextStateType); + UpdateContext(e.EventType, e.EventPayload); + if (CurrentState.EntryList != null && CurrentState.EntryList.Count > 0) + { + Dispatcher.dispatch(e.EventType, CurrentState.EntryList, this.Context); + } + + UpdateContext(e.EventType, e.EventPayload); + } + + } + + } + } + + public void Subscribe(List channels, List? channelGroups) + { + var evnt = new SubscriptionChanged(); + evnt.Name = "SUBSCRIPTION_CHANGED"; + evnt.EventType = EventType.SubscriptionChanged; + evnt.EventPayload.Channels = channels; + if (channelGroups != null) evnt.EventPayload.ChannelGroups = channelGroups; + this.Transition(evnt); + } + + private void UpdateContext(EventType eventType, EventPayload eventData) + { + if (CurrentState != null) + { + CurrentState.EventType = eventType; + } + if (eventData.Channels != null) Context.Channels = eventData.Channels; + if (eventData.ChannelGroups != null) Context.ChannelGroups = eventData.ChannelGroups; + if (eventData.Timetoken != null) + { + System.Diagnostics.Debug.WriteLine($"eventData.Timetoken = {eventData.Timetoken.Value}"); + System.Diagnostics.Debug.WriteLine($"Context.Timetoken = {Context.Timetoken.Value}"); + if (Context.Timetoken > 0 && + eventType == EventType.HandshakeSuccess && + Context.Timetoken < eventData.Timetoken) + { + System.Diagnostics.Debug.WriteLine("Keeping last Context.Timetoken"); + // do not change context timetoken. We want last timetoken. + } + else + { + Context.Timetoken = eventData.Timetoken; + } + } + if (eventData.Region != null) Context.Region = eventData.Region; + } + + public void InitialState(State state) + { + this.CurrentState = state; + } + + public State NextState() + { + State nextState = null; + if (CurrentState != null) + { + StateType nextStateType; + State findState = States.Find((s) => s.StateType == CurrentState.StateType); + if (findState != null && findState.transitions != null && findState.transitions.ContainsKey(CurrentState.EventType)) + { + nextStateType = findState.transitions[CurrentState.EventType]; + nextState = States.Find((s) => s.StateType == nextStateType); + } + } + return nextState; + } + + public void Setup(PNConfiguration config) + { + + + CreateState(StateType.Unsubscribed) + .On(EventType.SubscriptionChanged, StateType.Handshaking) + .On(EventType.SubscriptionRestored, StateType.Receiving); + + #region Handshake Effect Invocations and Emit Status + EmitStatus handshakeSuccessEmitStatus = new EmitStatus(); + handshakeSuccessEmitStatus.Name = "EMIT_STATUS"; + handshakeSuccessEmitStatus.Effectype = EventType.HandshakeSuccess; + + EffectInvocation handshakeInvocation = new Handshake(); + handshakeInvocation.Name = "HANDSHAKE"; + handshakeInvocation.Effectype = EventType.Handshake; + + EffectInvocation cancelHandshakeInvocation = new CancelHandshake(); + cancelHandshakeInvocation.Name = "CANCEL_HANDSHAKE"; + cancelHandshakeInvocation.Effectype = EventType.CancelHandshake; + #endregion + #region StateType.Handshaking + CreateState(StateType.Handshaking) + .On(EventType.SubscriptionChanged, StateType.Handshaking) + .On(EventType.HandshakeSuccess, StateType.Receiving, new List() + { + handshakeSuccessEmitStatus + } + ) + .On(EventType.HandshakeFailure, StateType.HandshakeReconnecting) + .On(EventType.Disconnect, StateType.HandshakeStopped) + .On(EventType.SubscriptionRestored, StateType.Receiving) + .OnEntry(entryInvocationList: new List() + { + handshakeInvocation + } + ) + .OnExit(exitInvocationList: new List() + { + cancelHandshakeInvocation + } + ); + #endregion + + #region HandshakeReconnecting Effect Invocations and Emit Status + EmitStatus handshakeReconnectSuccessEmitStatus = new EmitStatus(); + handshakeReconnectSuccessEmitStatus.Name = "EMIT_STATUS"; + handshakeReconnectSuccessEmitStatus.Effectype = EventType.HandshakeReconnectSuccess; + + //TBD - Should we emit status/error on HandshakeReconnectFailure + //EmitStatus handshakeReconnectFailureEmitStatus = new EmitStatus(); + // handshakeReconnectFailureEmitStatus.Name = "EMIT_STATUS"; + // handshakeReconnectFailureEmitStatus.Effectype = EventType.HandshakeReconnectFailure; + + EmitStatus handshakeReconnectGiveupEmitStatus = new EmitStatus(); + handshakeReconnectGiveupEmitStatus.Name = "EMIT_STATUS"; + handshakeReconnectGiveupEmitStatus.Effectype = EventType.HandshakeReconnectGiveUp; + + EffectInvocation handshakeReconnectInvocation = new HandshakeReconnect(); + handshakeReconnectInvocation.Name = "HANDSHAKE_RECONNECT"; + handshakeReconnectInvocation.Effectype = EventType.HandshakeReconnect; + + EffectInvocation cancelHandshakeReconnectInvocation = new CancelHandshakeReconnect(); + cancelHandshakeReconnectInvocation.Name = "CANCEL_HANDSHAKE_RECONNECT"; + cancelHandshakeReconnectInvocation.Effectype = EventType.CancelHandshakeReconnect; + #endregion + #region StateType.HandshakeReconnecting + CreateState(StateType.HandshakeReconnecting) + .On(EventType.SubscriptionChanged, StateType.Handshaking) + .On(EventType.HandshakeReconnectFailure, StateType.HandshakeReconnecting) + // .On(EventType.HandshakeReconnectFailure, StateType.HandshakeReconnecting, new List() + // { + // handshakeReconnectFailureEmitStatus + // } + //) + .On(EventType.Disconnect, StateType.HandshakeStopped) + .On(EventType.HandshakeReconnectGiveUp, StateType.HandshakeFailed, new List() + { + handshakeReconnectGiveupEmitStatus + } + ) + .On(EventType.HandshakeReconnectSuccess, StateType.Receiving, new List() + { + handshakeReconnectSuccessEmitStatus + } + ) + .On(EventType.SubscriptionRestored, StateType.Receiving) + .OnEntry(entryInvocationList: new List() + { + handshakeReconnectInvocation + } + ) + .OnExit(exitInvocationList: new List() + { + cancelHandshakeReconnectInvocation + } + ); + #endregion + + #region HandshakeFailed Effect Invocations and Emit Status + EffectInvocation handshakeFailedInvocation = new HandshakeFailed(); + handshakeFailedInvocation.Name = "HANDSHAKE_FAILED"; + handshakeFailedInvocation.Effectype = EventType.HandshakeFailure; + + EffectInvocation cancelHandshakeFailedInvocation = new CancelHandshakeFailed(); + cancelHandshakeFailedInvocation.Name = "CANCEL_HANDSHAKE_FAILED"; + cancelHandshakeFailedInvocation.Effectype = EventType.CancelHandshakeFailure; + #endregion + #region StateType.HandshakeFailed + CreateState(StateType.HandshakeFailed) + .On(EventType.SubscriptionChanged, StateType.Handshaking) + .On(EventType.Reconnect, StateType.Handshaking) + .On(EventType.SubscriptionRestored, StateType.Receiving) + .OnEntry(entryInvocationList: new List() + { + handshakeFailedInvocation + } + ) + .OnExit(exitInvocationList: new List() + { + cancelHandshakeFailedInvocation + } + ); + #endregion + + #region HandshakeStopped Effect Invocations and Emit Status + #endregion + #region StateType.HandshakeStopped + CreateState(StateType.HandshakeStopped) + .On(EventType.Reconnect, StateType.Handshaking); + #endregion + + #region Receiving Effect Invocations and Emit Status + EmitStatus receiveEmitStatus = new EmitStatus(); + receiveEmitStatus.Name = "EMIT_STATUS"; + receiveEmitStatus.Effectype = EventType.ReceiveSuccess; + + EmitMessages receiveEmitMessages = new EmitMessages(null); + receiveEmitMessages.Name = "EMIT_EVENTS"; + receiveEmitMessages.Effectype = EventType.ReceiveMessages; + + EmitStatus receiveDisconnectEmitStatus = new EmitStatus(); + receiveDisconnectEmitStatus.Name = "EMIT_STATUS"; + receiveDisconnectEmitStatus.Effectype = EventType.Disconnect; + + EffectInvocation receiveMessagesInvocation = new ReceiveMessages(); + receiveMessagesInvocation.Name = "RECEIVE_EVENTS"; + receiveMessagesInvocation.Effectype = EventType.ReceiveMessages; + + EffectInvocation cancelReceiveMessages = new CancelReceiveMessages(); + cancelReceiveMessages.Name = "CANCEL_RECEIVE_EVENTS"; + cancelReceiveMessages.Effectype = EventType.CancelReceiveMessages; + #endregion + #region StateType.Receiving + CreateState(StateType.Receiving) + .On(EventType.SubscriptionChanged, StateType.Receiving) + .On(EventType.SubscriptionRestored, StateType.Receiving) + .On(EventType.ReceiveSuccess, StateType.Receiving, new List() + { + receiveEmitStatus, + receiveEmitMessages + } + ) + .On(EventType.Disconnect, StateType.ReceiveStopped, new List() + { + receiveDisconnectEmitStatus + } + ) + .On(EventType.ReceiveFailure, StateType.ReceiveReconnecting) + .OnEntry(entryInvocationList: new List() + { + receiveMessagesInvocation + } + ) + .OnExit(exitInvocationList: new List() + { + cancelReceiveMessages + } + ); + #endregion + + #region ReceiveFailed Effect Invocations and Emit Status + #endregion + #region StateType.ReceiveFailed + CreateState(StateType.ReceiveFailed) + .On(EventType.SubscriptionChanged, StateType.Receiving) + .On(EventType.SubscriptionRestored, StateType.Receiving) + .On(EventType.Reconnect, StateType.Receiving); + #endregion + + #region ReceiveReconnecting Effect Invocations and Emit Status + EmitStatus receiveReconnectEmitStatus = new EmitStatus(); + receiveReconnectEmitStatus.Name = "RECONNECT_EMIT_STATUS"; + receiveReconnectEmitStatus.Effectype = EventType.ReceiveReconnectSuccess; + + EmitMessages receiveReconnectEmitMessages = new EmitMessages(null); + receiveReconnectEmitMessages.Name = "RECEIVE_RECONNECT_EVENTS"; + receiveReconnectEmitMessages.Effectype = EventType.ReceiveMessages; + + EmitStatus receiveReconnectDisconnectEmitStatus = new EmitStatus(); + receiveReconnectDisconnectEmitStatus.Name = "RECONNECT_DISCONNECT_STATUS"; + receiveReconnectDisconnectEmitStatus.Effectype = EventType.Disconnect; + + EmitStatus receiveReconnectGiveupEmitStatus = new EmitStatus(); + receiveReconnectGiveupEmitStatus.Name = "RECONNECT_GIVEUP_STATUS"; + receiveReconnectGiveupEmitStatus.Effectype = EventType.ReceiveReconnectGiveUp; + + EffectInvocation receiveReconnectInvocation = new ReceiveReconnect(); + receiveReconnectInvocation.Name = "RECEIVE_RECONNECT"; + receiveReconnectInvocation.Effectype = EventType.ReceiveReconnect; + + EffectInvocation cancelReceiveReconnect = new CancelReceiveReconnect(); + cancelReceiveReconnect.Name = "CANCEL_RECEIVE_RECONNECT"; + cancelReceiveReconnect.Effectype = EventType.CancelReceiveReconnect; + #endregion + #region StateType.ReceiveReconnecting + CreateState(StateType.ReceiveReconnecting) + .On(EventType.SubscriptionChanged, StateType.Receiving) + .On(EventType.ReceiveReconnectFailure, StateType.ReceiveReconnecting) + .On(EventType.SubscriptionRestored, StateType.Receiving) + .On(EventType.ReceiveReconnectSuccess, StateType.Receiving, new List() + { + //receiveReconnectEmitStatus, + receiveReconnectEmitMessages + } + ) + .On(EventType.Disconnect, StateType.ReceiveStopped) + .On(EventType.ReceiveReconnectGiveUp, StateType.ReceiveFailed, new List() + { + receiveReconnectGiveupEmitStatus + } + ) + .OnEntry(entryInvocationList: new List() + { + receiveReconnectInvocation + } + ) + .OnExit(exitInvocationList: new List() + { + cancelReceiveReconnect + } + ); + #endregion + + #region ReceiveStopped Effect Invocations and Emit Status + #endregion + #region StateType.ReceiveStopped + CreateState(StateType.ReceiveStopped) + .On(EventType.Reconnect, StateType.Receiving); + #endregion + + System.Diagnostics.Debug.WriteLine("EventEngine Setup done."); + } + + //public void SetCurrentStateType(StateType stateType, EventType eventType) + //{ + // State nextState = null; + // StateType nextStateType; + // IEnumerable eventTypeStates = States.Where(s => s.StateType == stateType && s.EventType == eventType); + // foreach (State state in eventTypeStates) + // { + // if (state.transitions.ContainsKey(eventType)) + // { + // nextStateType = state.transitions[eventType]; + // } + // } + //} + } +} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs new file mode 100644 index 000000000..a93d2326e --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs @@ -0,0 +1,185 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; + + +namespace PubnubApi.PubnubEventEngine +{ + public class HandshakeResponse + { + [JsonProperty("t")] + public Timetoken Timetoken { get; set; } + + [JsonProperty("m")] + public object[] Messages { get; set; } + } + public class HandshakeError + { + [JsonProperty("status")] + public int Status { get; set; } + + [JsonProperty("error")] + public string ErrorMessage { get; set; } + } + + public class Timetoken + { + [JsonProperty("t")] + public long? Timestamp { get; set; } + + [JsonProperty("r")] + public int? Region { get; set; } + + } + + public class HandshakeRequestEventArgs : EventArgs + { + public ExtendedState ExtendedState { get; set; } + public Action HandshakeResponseCallback { get; set; } + } + public class CancelHandshakeRequestEventArgs : EventArgs + { + } + + public class HandshakeEffectHandler : IEffectInvocationHandler + { + EventEmitter emitter; + private ExtendedState extendedState { get; set;} + public Action LogCallback { get; set; } + public Action AnnounceStatus { get; set; } + private PNStatus pnStatus { get; set; } + + public event EventHandler HandshakeRequested; + public event EventHandler CancelHandshakeRequested; + protected virtual void OnHandshakeRequested(HandshakeRequestEventArgs e) + { + EventHandler handler = HandshakeRequested; + if (handler != null) + { + handler(this, e); + } + } + protected virtual void OnCancelHandshakeRequested(CancelHandshakeRequestEventArgs e) + { + EventHandler handler = CancelHandshakeRequested; + if (handler != null) + { + handler(this, e); + } + } + + CancellationTokenSource cancellationTokenSource; + public HandshakeEffectHandler(EventEmitter emitter) + { + this.emitter = emitter; + cancellationTokenSource = new CancellationTokenSource(); + } + public async void Start(ExtendedState context, EventType eventType) + { + extendedState = context; + await Task.Factory.StartNew(() => { }); + if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { + Cancel(); + } + HandshakeRequestEventArgs args = new HandshakeRequestEventArgs(); + args.ExtendedState = context; + args.HandshakeResponseCallback = OnHandshakeEffectResponseReceived; + OnHandshakeRequested(args); + } + + public void OnHandshakeEffectResponseReceived(string json) + { + try + { + LogCallback?.Invoke($"OnHandshakeEffectResponseReceived Json Response = {json}"); + var handshakeResponse = JsonConvert.DeserializeObject(json); + if (handshakeResponse != null && handshakeResponse.Timetoken != null) + { + HandshakeSuccess handshakeSuccessEvent = new HandshakeSuccess(); + handshakeSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); + handshakeSuccessEvent.SubscriptionCursor.Timetoken = handshakeResponse.Timetoken?.Timestamp; + handshakeSuccessEvent.SubscriptionCursor.Region = handshakeResponse.Timetoken?.Region; + + handshakeSuccessEvent.EventPayload.Timetoken = handshakeResponse.Timetoken?.Timestamp; + handshakeSuccessEvent.EventPayload.Region = handshakeResponse.Timetoken?.Region; + handshakeSuccessEvent.EventType = EventType.HandshakeSuccess; + handshakeSuccessEvent.Name = "HANDSHAKE_SUCCESS"; + LogCallback?.Invoke("OnHandshakeEffectResponseReceived - EventType.HandshakeSuccess"); + + pnStatus = new PNStatus(); + pnStatus.StatusCode = 200; + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNConnectedCategory; + pnStatus.Error = false; + + emitter.emit(handshakeSuccessEvent); + } + else + { + HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); + handshakeFailureEvent.Name = "HANDSHAKE_FAILURE"; + handshakeFailureEvent.EventType = EventType.HandshakeFailure; + LogCallback?.Invoke("OnHandshakeEffectResponseReceived - EventType.HandshakeFailure"); + + pnStatus = new PNStatus(); + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; + pnStatus.Error = true; + + emitter.emit(handshakeFailureEvent); + } + } + catch (Exception ex) + { + LogCallback?.Invoke($"OnHandshakeEffectResponseReceived EXCEPTION - {ex}"); + HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); + handshakeFailureEvent.Name = "HANDSHAKE_FAILURE"; + handshakeFailureEvent.EventType = EventType.HandshakeFailure; + handshakeFailureEvent.EventPayload.exception = ex; + + pnStatus = new PNStatus(); + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; + pnStatus.Error = true; + + emitter.emit(handshakeFailureEvent); + } + + //emitter.emit(json, true, 0); + } + public void Cancel() + { + if (cancellationTokenSource != null) + { + LogCallback?.Invoke($"HandshakeEffectHandler - cancellationTokenSource - cancellion attempted."); + cancellationTokenSource.Cancel(); + } + + LogCallback?.Invoke($"HandshakeEffectHandler - invoking OnCancelHandshakeRequested."); + CancelHandshakeRequestEventArgs args = new CancelHandshakeRequestEventArgs(); + OnCancelHandshakeRequested(args); + } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + } + + public PNStatus GetPNStatus() + { + return pnStatus; + } + + } + + +} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs new file mode 100644 index 000000000..52100ca4a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Threading; + +namespace PubnubApi.PubnubEventEngine +{ + public class HandshakeFailedEffectHandler : IEffectInvocationHandler + { + EventEmitter emitter; + //public EffectInvocationType InvocationType { get; set; } + private ExtendedState extendedState { get; set;} + private PNStatus pnStatus { get; set; } + public Action LogCallback { get; set; } + public Action AnnounceStatus { get; set; } + + CancellationTokenSource? cancellationTokenSource; + + public HandshakeFailedEffectHandler(EventEmitter emitter) + { + this.emitter = emitter; + cancellationTokenSource = new CancellationTokenSource(); + } + + public async void Start(ExtendedState context, EventType eventType) + { + extendedState = context; + await Task.Factory.StartNew(() => { }); + if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { + Cancel(); + } + + if (eventType != EventType.HandshakeReconnectGiveUp) + { + LogCallback?.Invoke("HandshakeFailedEffectHandler - EventType.Handshake"); + Reconnect reconnectEvent = new Reconnect(); + reconnectEvent.Name = "RECONNECT"; + reconnectEvent.EventType = EventType.Reconnect; + + emitter.emit(reconnectEvent); + } + + } + + public void Cancel() + { + if (cancellationTokenSource != null) + { + LogCallback?.Invoke($"HandshakeFailedEffectHandler - cancellationTokenSource - cancellion attempted."); + cancellationTokenSource.Cancel(); + } + } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + } + + public PNStatus GetPNStatus() + { + return pnStatus; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs new file mode 100644 index 000000000..e8fa53f5c --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs @@ -0,0 +1,274 @@ +using Newtonsoft.Json; +using PubnubApi.PubnubEventEngine; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + + +namespace PubnubApi.PubnubEventEngine +{ + public class HandshakeReconnectRequestEventArgs : EventArgs + { + public ExtendedState ExtendedState { get; set; } + public Action HandshakeReconnectResponseCallback { get; set; } + } + public class CancelHandshakeReconnectRequestEventArgs : EventArgs + { + } + public class HandshakeReconnectEffectHandler : IEffectInvocationHandler + { + EventEmitter emitter; + private ExtendedState extendedState { get; set;} + public Action LogCallback { get; set; } + public Action AnnounceStatus { get; set; } + public PNReconnectionPolicy ReconnectionPolicy { get; set; } + public int MaxRetries { get; set; } + private PNStatus pnStatus { get; set; } + private int timerInterval; + const int MINEXPONENTIALBACKOFF = 1; + const int MAXEXPONENTIALBACKOFF = 25; + const int INTERVAL = 3; + + public event EventHandler HandshakeReconnectRequested; + public event EventHandler CancelHandshakeReconnectRequested; + System.Threading.Timer timer; + protected virtual void OnHandshakeReconnectRequested(HandshakeReconnectRequestEventArgs e) + { + EventHandler handler = HandshakeReconnectRequested; + if (handler != null) + { + handler(this, e); + } + } + protected virtual void OnCancelHandshakeReconnectRequested(CancelHandshakeReconnectRequestEventArgs e) + { + EventHandler handler = CancelHandshakeReconnectRequested; + if (handler != null) + { + handler(this, e); + } + } + + CancellationTokenSource cancellationTokenSource; + public HandshakeReconnectEffectHandler(EventEmitter emitter) + { + this.emitter = emitter; + cancellationTokenSource = new CancellationTokenSource(); + } + + public async void Start(ExtendedState context, EventType eventType) + { + extendedState = context; + await Task.Factory.StartNew(() => { }); + if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { + Cancel(); + } + + if (ReconnectionPolicy == PNReconnectionPolicy.EXPONENTIAL) + { + double numberForMath = extendedState.Attempts % 6; + timerInterval = (int)(Math.Pow(2, numberForMath) - 1); + if (timerInterval > MAXEXPONENTIALBACKOFF) + { + timerInterval = MINEXPONENTIALBACKOFF; + } + else if (timerInterval < 1) + { + timerInterval = MINEXPONENTIALBACKOFF; + } + } + else if (ReconnectionPolicy == PNReconnectionPolicy.LINEAR) + { + timerInterval = INTERVAL; + } + else + { + timerInterval = -1; + } + LogCallback?.Invoke($"HandshakeReconnectEffectHandler ReconnectionPolicy = {ReconnectionPolicy}; Interval = {timerInterval}"); + + if (timer != null) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + if (timerInterval != -1) + { + timer = new Timer(new TimerCallback(HandshakeReconnectTimerCallback), null, + (-1 == timerInterval) ? Timeout.Infinite : timerInterval * 1000, Timeout.Infinite); + } + else + { + PrepareFailurePNStatus(new HandshakeError() { Status = 400 }); + PrepareAndEmitHandshakeReconnectGiveupEvent(null); + } + } + + private void HandshakeReconnectTimerCallback(object state) + { + LogCallback?.Invoke("HandshakeReconnectEffectHandler Timer interval invoke"); + HandshakeReconnectRequestEventArgs args = new HandshakeReconnectRequestEventArgs(); + args.ExtendedState = extendedState; + args.HandshakeReconnectResponseCallback = OnHandshakeReconnectEffectResponseReceived; + OnHandshakeReconnectRequested(args); + } + + public void OnHandshakeReconnectEffectResponseReceived(string json) + { + try + { + LogCallback?.Invoke($"OnHandshakeReconnectEffectResponseReceived Json Response = {json}"); + var handshakeResponse = JsonConvert.DeserializeObject(json); + if (handshakeResponse != null && handshakeResponse.Timetoken != null) + { + HandshakeReconnectSuccess handshakeReconnectSuccessEvent = new HandshakeReconnectSuccess(); + handshakeReconnectSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); + handshakeReconnectSuccessEvent.SubscriptionCursor.Timetoken = handshakeResponse.Timetoken?.Timestamp; + handshakeReconnectSuccessEvent.SubscriptionCursor.Region = handshakeResponse.Timetoken?.Region; + + handshakeReconnectSuccessEvent.EventPayload.Timetoken = handshakeResponse.Timetoken?.Timestamp; + handshakeReconnectSuccessEvent.EventPayload.Region = handshakeResponse.Timetoken?.Region; + handshakeReconnectSuccessEvent.EventType = EventType.HandshakeReconnectSuccess; + handshakeReconnectSuccessEvent.Name = "HANDSHAKE_RECONNECT_SUCCESS"; + LogCallback?.Invoke("OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectSuccess"); + + pnStatus = new PNStatus(); + pnStatus.StatusCode = 200; + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNConnectedCategory; + pnStatus.Error = false; + + extendedState.Attempts = 0; + + emitter.emit(handshakeReconnectSuccessEvent); + } + else + { + var handshakeError = JsonConvert.DeserializeObject(json); + extendedState.Attempts++; + PrepareFailurePNStatus(handshakeError); + + if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectGiveUp"); + PrepareAndEmitHandshakeReconnectGiveupEvent(null); + } + else + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectFailure"); + PrepareAndEmitHandshakeReconnectFailureEvent(null); + } + } + } + catch (Exception ex) + { + extendedState.Attempts++; + PrepareFailurePNStatus(new HandshakeError() { Status = 400 }); + + if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectGiveUp"); + PrepareAndEmitHandshakeReconnectGiveupEvent(null); + + } + else + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectFailure"); + PrepareAndEmitHandshakeReconnectFailureEvent(ex); + } + } + finally + { + if (timer != null) + { + try + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + catch { } + } + } + } + + private void PrepareFailurePNStatus(HandshakeError error) + { + pnStatus = new PNStatus(); + pnStatus.StatusCode = (error.Status != 0) ? error.Status : 504; + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; + pnStatus.Error = true; + } + + private void PrepareAndEmitHandshakeReconnectFailureEvent(Exception ex) + { + HandshakeReconnectFailure handshakeReconnectFailureEvent = new HandshakeReconnectFailure(); + handshakeReconnectFailureEvent.Name = "HANDSHAKE_RECONNECT_FAILURE"; + handshakeReconnectFailureEvent.EventType = EventType.HandshakeReconnectFailure; + handshakeReconnectFailureEvent.Attempts = extendedState.Attempts; + if (ex != null) + { + handshakeReconnectFailureEvent.EventPayload.exception = ex; + } + + emitter.emit(handshakeReconnectFailureEvent); + } + + private void PrepareAndEmitHandshakeReconnectGiveupEvent(Exception ex) + { + HandshakeReconnectGiveUp handshakeReconnectGiveupEvent = new HandshakeReconnectGiveUp(); + handshakeReconnectGiveupEvent.Name = "HANDSHAKE_RECONNECT_GIVEUP"; + handshakeReconnectGiveupEvent.EventType = EventType.HandshakeReconnectGiveUp; + handshakeReconnectGiveupEvent.Attempts = extendedState.Attempts; + if (ex != null) + { + handshakeReconnectGiveupEvent.EventPayload.exception = ex; + } + + emitter.emit(handshakeReconnectGiveupEvent); + } + + //private void PrepareAndEmitHandshakeFailedEvent(Exception ex) + //{ + // HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); + // handshakeFailureEvent.Name = "HANDSHAKE_FAILURE"; + // handshakeFailureEvent.EventType = EventType.HandshakeReconnectGiveUp; + // handshakeFailureEvent.Attempts = extendedState.Attempts; + // if (ex != null) + // { + // handshakeFailureEvent.EventPayload.exception = ex; + // } + + // emitter.emit(handshakeFailureEvent); + //} + public void Cancel() + { + if (cancellationTokenSource != null) + { + LogCallback?.Invoke($"HandshakeReconnectEffectHandler - cancellationTokenSource - cancellion attempted."); + cancellationTokenSource.Cancel(); + } + LogCallback?.Invoke($"HandshakeReconnectEffectHandler - invoking OnCancelHandshakeReconnectRequested."); + CancelHandshakeReconnectRequestEventArgs args = new CancelHandshakeReconnectRequestEventArgs(); + OnCancelHandshakeReconnectRequested(args); + } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + } + + public PNStatus GetPNStatus() + { + return pnStatus; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs b/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs new file mode 100644 index 000000000..4350f3a22 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs @@ -0,0 +1,37 @@ +using System; + +namespace PubnubApi.PubnubEventEngine +{ + + public enum EffectInvocationType + { + Handshake, + HandshakeSuccess, + CancelHandshake, + ReceiveMessages, + ReceiveSuccess, + Disconnect, + HandshakeReconnect, + HandshakeReconnectSuccess, + CancelHandshakeReconnect, + CancelReceiveMessages, + ReceiveReconnect, + ReceiveReconnectSuccess, + ReceiveReconnectGiveUp, + CancelReceiveReconnect, + ReconnectionAttempt + } + + public interface IEffectInvocationHandler + { + void Start(ExtendedState context, EventType eventType); + void Cancel(); + void Run(ExtendedState context); + PNStatus GetPNStatus(); + } + + public interface IReceiveMessageHandler + { + Message[] GetMessages(); + } +} diff --git a/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs new file mode 100644 index 000000000..6e8872253 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs @@ -0,0 +1,273 @@ +using Newtonsoft.Json; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace PubnubApi.PubnubEventEngine +{ + public class ReceiveReconnectRequestEventArgs : EventArgs + { + public ExtendedState ExtendedState { get; set; } + public Action ReceiveReconnectResponseCallback { get; set; } + } + public class CancelReceiveReconnectRequestEventArgs : EventArgs + { + } + public class ReceiveReconnectingEffectHandler : IEffectInvocationHandler + { + EventEmitter eventEmitter; + private ExtendedState extendedState { get; set;} + public Action LogCallback { get; set; } + public Action AnnounceStatus { get; set; } + public PNReconnectionPolicy ReconnectionPolicy { get; set; } + public int MaxRetries { get; set; } + private Message[] receiveMessages { get; set; } + private PNStatus pnStatus { get; set; } + private int timerInterval; + const int MINEXPONENTIALBACKOFF = 1; + const int MAXEXPONENTIALBACKOFF = 25; + const int INTERVAL = 3; + + public event EventHandler ReceiveReconnectRequested; + public event EventHandler CancelReceiveReconnectRequested; + System.Threading.Timer timer; + protected virtual void OnReceiveReconnectRequested(ReceiveReconnectRequestEventArgs e) + { + EventHandler handler = ReceiveReconnectRequested; + if (handler != null) + { + handler(this, e); + } + } + protected virtual void OnCancelReceiveReconnectRequested(CancelReceiveReconnectRequestEventArgs e) + { + EventHandler handler = CancelReceiveReconnectRequested; + if (handler != null) + { + handler(this, e); + } + } + + CancellationTokenSource cancellationTokenSource; + public ReceiveReconnectingEffectHandler(EventEmitter emitter) + { + this.eventEmitter = emitter; + cancellationTokenSource = new CancellationTokenSource(); + } + + public async void Start(ExtendedState context, EventType eventType) + { + extendedState = context; + await Task.Factory.StartNew(() => { }); + if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { + Cancel(); + } + + if (ReconnectionPolicy == PNReconnectionPolicy.EXPONENTIAL) + { + double numberForMath = extendedState.Attempts % 6; + timerInterval = (int)(Math.Pow(2, numberForMath) - 1); + if (timerInterval > MAXEXPONENTIALBACKOFF) + { + timerInterval = MINEXPONENTIALBACKOFF; + } + else if (timerInterval < 1) + { + timerInterval = MINEXPONENTIALBACKOFF; + } + } + else if (ReconnectionPolicy == PNReconnectionPolicy.LINEAR) + { + timerInterval = INTERVAL; + } + else + { + timerInterval = -1; + } + LogCallback?.Invoke($"ReceiveReconnectingEffectHandler ReconnectionPolicy = {ReconnectionPolicy}; Interval = {timerInterval}"); + + if (timer != null) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + if (timerInterval != -1) + { + timer = new Timer(new TimerCallback(ReceiveReconnectTimerCallback), null, + (-1 == timerInterval) ? Timeout.Infinite : timerInterval * 1000, Timeout.Infinite); + } + else + { + PrepareFailurePNStatus(new ReceiveError() { Status = 400 }); + PrepareAndEmitReceiveReconnectGiveupEvent(null); + } + } + + private void ReceiveReconnectTimerCallback(object state) + { + LogCallback?.Invoke("ReceiveReconnectingEffectHandler Timer interval invoke"); + ReceiveReconnectRequestEventArgs args = new ReceiveReconnectRequestEventArgs(); + args.ExtendedState = extendedState; + args.ReceiveReconnectResponseCallback = OnReceiveReconnectEffectResponseReceived; + OnReceiveReconnectRequested(args); + } + + public void OnReceiveReconnectEffectResponseReceived(string json) + { + try + { + pnStatus = null; + LogCallback?.Invoke($"OnReceiveReconnectEffectResponseReceived Json Response = {json}"); + var receivedResponse = JsonConvert.DeserializeObject>(json); + if (receivedResponse != null && receivedResponse.Timetoken != null) + { + receiveMessages = receivedResponse.Messages; + + ReceiveReconnectSuccess receiveReconnectSuccessEvent = new ReceiveReconnectSuccess(); + receiveReconnectSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); + receiveReconnectSuccessEvent.SubscriptionCursor.Timetoken = receivedResponse.Timetoken.Timestamp; + receiveReconnectSuccessEvent.SubscriptionCursor.Region = receivedResponse.Timetoken.Region; + receiveReconnectSuccessEvent.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; + receiveReconnectSuccessEvent.EventPayload.Region = receivedResponse.Timetoken.Region; + receiveReconnectSuccessEvent.EventType = EventType.ReceiveReconnectSuccess; + receiveReconnectSuccessEvent.Name = "RECEIVE_RECONNECT_SUCCESS"; + LogCallback?.Invoke("OnReceiveReconnectEffectResponseReceived - EventType.ReceiveReconnectSuccess"); + + pnStatus = new PNStatus(); + pnStatus.StatusCode = 200; + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNConnectedCategory; + pnStatus.Error = false; + + extendedState.Attempts = 0; + + eventEmitter.emit(receiveReconnectSuccessEvent); + } + else + { + ReceiveReconnectFailure receiveReconnectFailureEvent = new ReceiveReconnectFailure(); + receiveReconnectFailureEvent.Name = "RECEIVE_RECONNECT_FAILURE"; + receiveReconnectFailureEvent.EventType = EventType.ReceiveReconnectFailure; + LogCallback?.Invoke("OnReceivingReconnectEffectResponseReceived - EventType.ReceiveReconnectFailure"); + + pnStatus = new PNStatus(); + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Error = true; + + + var receiveReconnectError = JsonConvert.DeserializeObject(json); + extendedState.Attempts++; + PrepareFailurePNStatus(receiveReconnectError); + + if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnReceivingReconnectEffectResponseReceived - EventType.ReceiveReconnectGiveUp"); + PrepareAndEmitReceiveReconnectGiveupEvent(null); + } + else + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnReceivingReconnectEffectResponseReceived - EventType.ReceiveReconnectFailure"); + PrepareAndEmitReceiveReconnectFailureEvent(null); + } + } + } + catch (Exception ex) + { + extendedState.Attempts++; + PrepareFailurePNStatus(new ReceiveError() { Status = 400 }); + + if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.ReceiveReconnectGiveUp"); + PrepareAndEmitReceiveReconnectGiveupEvent(null); + + } + else + { + LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.ReceiveReconnectFailure"); + PrepareAndEmitReceiveReconnectFailureEvent(ex); + } + } + finally + { + if (timer != null) + { + try + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + catch { } + } + } + } + + private void PrepareFailurePNStatus(ReceiveError error) + { + pnStatus = new PNStatus(); + pnStatus.StatusCode = (error != null && error.Status != 0) ? error.Status : 504; + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; + pnStatus.Error = true; + } + + private void PrepareAndEmitReceiveReconnectFailureEvent(Exception ex) + { + ReceiveReconnectFailure receiveReconnectFailureEvent = new ReceiveReconnectFailure(); + receiveReconnectFailureEvent.Name = "RECEIVE_RECONNECT_FAILURE"; + receiveReconnectFailureEvent.EventType = EventType.ReceiveReconnectFailure; + receiveReconnectFailureEvent.Attempts = extendedState.Attempts; + if (ex != null) + { + receiveReconnectFailureEvent.EventPayload.exception = ex; + } + + eventEmitter.emit(receiveReconnectFailureEvent); + } + + private void PrepareAndEmitReceiveReconnectGiveupEvent(Exception ex) + { + ReceiveReconnectGiveUp receiveReconnectGiveupEvent = new ReceiveReconnectGiveUp(); + receiveReconnectGiveupEvent.Name = "RECEIVE_RECONNECT_GIVEUP"; + receiveReconnectGiveupEvent.EventType = EventType.ReceiveReconnectGiveUp; + receiveReconnectGiveupEvent.Attempts = extendedState.Attempts; + if (ex != null) + { + receiveReconnectGiveupEvent.EventPayload.exception = ex; + } + + eventEmitter.emit(receiveReconnectGiveupEvent); + } + + public void Cancel() + { + if (cancellationTokenSource != null) + { + LogCallback?.Invoke($"ReceiveReconnectEffectHandler - cancellationTokenSource - cancellion attempted."); + cancellationTokenSource.Cancel(); + } + if (timer != null) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + LogCallback?.Invoke($"ReceiveReconnectEffectHandler - invoking OnCancelReceiveReconnectRequested."); + CancelReceiveReconnectRequestEventArgs args = new CancelReceiveReconnectRequestEventArgs(); + OnCancelReceiveReconnectRequested(args); + } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null && pnStatus != null) + { + AnnounceStatus(pnStatus); + } + } + public PNStatus GetPNStatus() + { + return pnStatus; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs new file mode 100644 index 000000000..1c719582c --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -0,0 +1,294 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace PubnubApi.PubnubEventEngine +{ + public class ReceiveingResponse + { + [JsonProperty("t")] + public Timetoken Timetoken { get; set; } + + [JsonProperty("m")] + public Message[] Messages { get; set; } + } + + public class Message + { + [JsonProperty ("a")] + public string Shard { get; set;} + + [JsonProperty ("b")] + public string SubscriptionMatch { get; set;} + + [JsonProperty("c")] + public string Channel { get; set; } + + [JsonProperty("d")] + public T Payload { get; set; } + + [JsonProperty("e")] + public int MessageType { get; set; } + + [JsonProperty("f")] + public string Flags { get; set; } + + //[JsonProperty("i")] + //public string IssuingClientId { get; set; } + + [JsonProperty("k")] + public string SubscribeKey { get; set; } + + [JsonProperty("o")] + public object OriginatingTimetoken { get; set; } + + [JsonProperty("p")] + public object PublishMetadata { get; set; } + + [JsonProperty("s")] + public long SequenceNumber { get; set; } + } + + public class PresenceEvent + { + [JsonProperty("action")] + public string Action { get; set; } + + [JsonProperty("uuid")] + public string Uuid { get; set; } + + [JsonProperty("timestamp")] + public long Timestamp { get; set; } + + [JsonProperty("occupancy")] + public int Occupancy { get; set; } + + } + + public class ReceiveError + { + [JsonProperty("status")] + public int Status { get; set; } + + [JsonProperty("error")] + public string ErrorMessage { get; set; } + } + + public class ReceiveRequestEventArgs : EventArgs + { + public ExtendedState ExtendedState { get; set; } + public Action ReceiveResponseCallback { get; set; } + } + + public class CancelReceiveRequestEventArgs : EventArgs + { + + } + + public class ReceivingEffectHandler : IEffectInvocationHandler, IReceiveMessageHandler + { + EventEmitter emitter; + private ExtendedState extendedState { get; set;} + private PNStatus pnStatus { get; set; } + private Message[] receiveMessages { get; set; } + public Action LogCallback { get; set; } + public Action AnnounceStatus { get; set; } + public Action> AnnounceMessage { get; set; } + public Action AnnouncePresenceEvent { get; set; } + public PNReconnectionPolicy ReconnectionPolicy { get; set; } + + public event EventHandler ReceiveRequested; + public event EventHandler CancelReceiveRequested; + protected virtual void OnReceiveRequested(ReceiveRequestEventArgs e) + { + EventHandler handler = ReceiveRequested; + if (handler != null) + { + handler(this, e); + } + } + + protected virtual void OnCancelReceiveRequested(CancelReceiveRequestEventArgs e) + { + EventHandler handler = CancelReceiveRequested; + if (handler != null) + { + handler(this, e); + } + } + + CancellationTokenSource cancellationTokenSource; + + public ReceivingEffectHandler(EventEmitter emitter) + { + this.emitter = emitter; + cancellationTokenSource = new CancellationTokenSource(); + } + + public async void Start(ExtendedState context, EventType eventType) + { + extendedState = context; + await Task.Factory.StartNew(() => { }); + if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { + Cancel(); + } + cancellationTokenSource = new CancellationTokenSource(); + + await Task.Factory.StartNew(() => { }); + ReceiveRequestEventArgs args = new ReceiveRequestEventArgs(); + args.ExtendedState = context; + args.ReceiveResponseCallback = OnReceivingEffectResponseReceived; + OnReceiveRequested(args); + } + + public void OnReceivingEffectResponseReceived(string json) + { + try + { + pnStatus = null; + var receivedResponse = JsonConvert.DeserializeObject>(json); + if (receivedResponse != null && receivedResponse.Timetoken != null) + { + receiveMessages = receivedResponse.Messages; + + ReceiveSuccess receiveSuccessEvent = new ReceiveSuccess(); + receiveSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); + receiveSuccessEvent.SubscriptionCursor.Timetoken = receivedResponse.Timetoken.Timestamp; + receiveSuccessEvent.SubscriptionCursor.Region = receivedResponse.Timetoken.Region; + receiveSuccessEvent.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; + receiveSuccessEvent.EventPayload.Region = receivedResponse.Timetoken.Region; + receiveSuccessEvent.EventType = EventType.ReceiveSuccess; + receiveSuccessEvent.Name = "RECEIVE_SUCCESS"; + LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveSuccess"); + + emitter.emit(receiveSuccessEvent); + } + else + { + ReceiveFailure receiveFailureEvent = new ReceiveFailure(); + receiveFailureEvent.Name = "RECEIVE_FAILURE"; + receiveFailureEvent.EventType = EventType.ReceiveFailure; + LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveFailure"); + + pnStatus = new PNStatus(); + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Error = true; + + emitter.emit(receiveFailureEvent); + } + } + catch (Exception ex) + { + LogCallback?.Invoke($"ReceivingEffectHandler EXCEPTION - {ex}"); + + ReceiveFailure receiveFailureEvent = new ReceiveFailure(); + receiveFailureEvent.Name = "RECEIVE_FAILURE"; + receiveFailureEvent.EventType = EventType.ReceiveFailure; + receiveFailureEvent.EventPayload.exception = ex; + LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveFailure"); + + pnStatus = new PNStatus(); + pnStatus.Operation = PNOperationType.PNSubscribeOperation; + pnStatus.AffectedChannels = extendedState.Channels; + pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; + pnStatus.Error = true; + + emitter.emit(receiveFailureEvent); + } + } + + public void Cancel() + { + if (cancellationTokenSource != null) + { + LogCallback?.Invoke($"ReceivingEffectHandler - Receiving request cancellion attempted."); + cancellationTokenSource.Cancel(); + } + LogCallback?.Invoke($"ReceivingEffectHandler - invoking OnCancelReceiveRequested."); + CancelReceiveRequestEventArgs args = new CancelReceiveRequestEventArgs(); + OnCancelReceiveRequested(args); + } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null && pnStatus != null) + { + AnnounceStatus(pnStatus); + } + Message[] receiveMessages = GetMessages(); + int messageCount = (receiveMessages != null) ? receiveMessages.Length : 0; + if (messageCount > 0) + { + for (int index = 0; index < receiveMessages.Length; index++) + { + LogCallback?.Invoke($"Received Message ({index + 1} of {receiveMessages.Length}) : {JsonConvert.SerializeObject(receiveMessages[index])}"); + if (receiveMessages[index].Channel.IndexOf("-pnpres") > 0) + { + if (AnnouncePresenceEvent != null) + { + var presenceEvent = JsonConvert.DeserializeObject(receiveMessages[index].Payload.ToString()); + PNPresenceEventResult presenceEventResult = new PNPresenceEventResult(); + presenceEventResult.Channel = receiveMessages[index].Channel; + presenceEventResult.Event = presenceEvent.Action; + presenceEventResult.Occupancy = presenceEvent.Occupancy; + presenceEventResult.Uuid = presenceEvent.Uuid; + presenceEventResult.Timestamp = presenceEvent.Timestamp; + presenceEventResult.UserMetadata = receiveMessages[index].PublishMetadata; + + AnnouncePresenceEvent?.Invoke(presenceEventResult); + } + } + else + { + if (receiveMessages[index].MessageType == 1) + { + //TODO: Callback for Signal message + PNSignalResult signalMessage = new PNSignalResult + { + Channel = receiveMessages[index].Channel, + Message = receiveMessages[index].Payload, + }; + AnnounceMessage?.Invoke(signalMessage); + } + else if (receiveMessages[index].MessageType == 2) + { + //TODO: Callback for Object message + } + else if (receiveMessages[index].MessageType == 3) + { + //TODO: Callback for Message Action message + } + else if (receiveMessages[index].MessageType == 4) + { + //TODO: Callback for File message + } + else + { + //Callback for regular message + if (AnnounceMessage != null) + { + LogCallback?.Invoke($"Message : {JsonConvert.SerializeObject(receiveMessages[index].Payload)}"); + PNMessageResult messageResult = new PNMessageResult(); + messageResult.Channel = receiveMessages[index].Channel; + messageResult.Message = receiveMessages[index].Payload; + AnnounceMessage?.Invoke(messageResult); + } + } + } + } + } + } + + public PNStatus GetPNStatus() + { + return pnStatus; + } + + public Message[] GetMessages() + { + return receiveMessages; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/State.cs b/src/Api/PubnubApi/EventEngine/State.cs new file mode 100644 index 000000000..9bbbe1e26 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/State.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +namespace PubnubApi.PubnubEventEngine +{ + + public enum StateType { Unsubscribed, Handshaking, HandshakingFailed, Receiving, ReceiveReconnecting, ReceiveStopped, ReceiveFailed, HandshakeFailed, HandshakeReconnecting, HandshakeStopped }; + + public class State + { + public EventType EventType { get; set; } + public StateType StateType { get; set; } + + public Dictionary transitions; + public Dictionary> EffectInvocationsList { get; private set; } + public List EntryList { get; private set; } + + public List ExitList { get; private set; } + + public State(StateType type) + { + this.StateType = type; + this.transitions = new Dictionary(); + //EffectInvocationsList = new List(); + EffectInvocationsList = new Dictionary>(); + } + + public State On(EventType e, StateType nextState) + { + transitions.Add(e, nextState); + return this; + } + public State On(EventType e, StateType nextState, List effectInvocation) + { + transitions.Add(e, nextState); + EffectInvocationsList.Add(e, effectInvocation); + return this; + } + + public State OnEntry(List entryInvocationList) + { + this.EntryList = entryInvocationList; + return this; + } + + public State OnExit(List exitInvocationList) + { + this.ExitList = exitInvocationList; + return this; + } + + //public State EffectInvocation(EffectInvocationType trigger, IEffectInvocationHandler effectInvocationHandler) + //{ + // this.EffectInvocationsList.Add(new EffectInvocation() { Effectype=trigger, Handler = effectInvocationHandler}); + // return this; + //} + } +} diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs new file mode 100644 index 000000000..c3826ca42 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs @@ -0,0 +1,94 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using PubnubApi.EventEngine.Subscribe.Context; + +namespace PubnubApi.EventEngine.Subscribe.Common +{ + public class SubscriptionCursor + { + public long? Timetoken { get; set; } + public int? Region { get; set; } + } + + public class HandshakeResponse + { + [JsonProperty("t")] + public Timetoken Timetoken { get; set; } + + [JsonProperty("m")] + public object[] Messages { get; set; } + } + public class HandshakeError + { + [JsonProperty("status")] + public int Status { get; set; } + + [JsonProperty("error")] + public string ErrorMessage { get; set; } + } + + public class Timetoken + { + [JsonProperty("t")] + public long Timestamp { get; set; } + + [JsonProperty("r")] + public int Region { get; set; } + + } + + public class ReceivingResponse + { + [JsonProperty("t")] + public Timetoken Timetoken { get; set; } + + [JsonProperty("m")] + public Message[] Messages { get; set; } + } + + public class Message + { + [JsonProperty ("a")] + public string Shard { get; set;} + + [JsonProperty ("b")] + public string SubscriptionMatch { get; set;} + + [JsonProperty("c")] + public string Channel { get; set; } + + [JsonProperty("d")] + public T Payload { get; set; } + + [JsonProperty("e")] + public int MessageType { get; set; } + + [JsonProperty("f")] + public string Flags { get; set; } + + [JsonProperty("i")] + public string IssuingClientId { get; set; } + + [JsonProperty("k")] + public string SubscribeKey { get; set; } + + [JsonProperty("o")] + public object OriginatingTimetoken { get; set; } + + [JsonProperty("p")] + public object PublishMetadata { get; set; } + + [JsonProperty("s")] + public long SequenceNumber { get; set; } + + [JsonProperty("p")] + public Timetoken Timetoken { get; set; } + } + + public abstract class SubscriptionState : Core.State + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public ReconnectionConfiguration ReconnectionConfiguration; + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs new file mode 100644 index 000000000..7134c2891 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs @@ -0,0 +1,16 @@ +using System; +namespace PubnubApi.EventEngine.Subscribe.Context +{ + public class ReconnectionConfiguration + { + public PNReconnectionPolicy ReconnectionPolicy { get; set; } + public int MaximumReconnectionRetries { get; set; } + + public ReconnectionConfiguration(PNReconnectionPolicy policy, int maximumReconnectionRetries) + { + this.ReconnectionPolicy = policy; + this.MaximumReconnectionRetries = maximumReconnectionRetries; + } + } +} + diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs new file mode 100644 index 000000000..393002481 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs @@ -0,0 +1,31 @@ +using System; +namespace PubnubApi.EventEngine.Subscribe.Context +{ + public static class ReconnectionDelayUtil + { + public static int CalculateDelay(PNReconnectionPolicy policy, int attempts) + { + Random numGenerator = new Random(); + int delayValue = 0; + int backoff = 5; + switch (policy) { + case PNReconnectionPolicy.LINEAR: + delayValue = attempts * backoff + numGenerator.Next(1000); + break; + case PNReconnectionPolicy.EXPONENTIAL: + delayValue = (int)(Math.Pow(2, attempts - 1) * 1000 + numGenerator.Next(1000)); + break; + } + return delayValue; + + } + + public static bool shouldRetry(ReconnectionConfiguration reconnectionConfiguration, int attemptedRetries) + { + if (reconnectionConfiguration.ReconnectionPolicy == PNReconnectionPolicy.NONE) return false; + if (reconnectionConfiguration.MaximumReconnectionRetries < 0) return true; + return reconnectionConfiguration.MaximumReconnectionRetries < attemptedRetries; + } + } +} + diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs new file mode 100644 index 000000000..e946b5c4a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.Effects +{ + public class EmitMessagesHandler : EffectHandler + { + private readonly System.Action> messageEmitterFunction; + private readonly Pubnub pubnubInstance; + + public EmitMessagesHandler(Pubnub pubnubInstance, + System.Action> messageEmitterFunction) + { + this.messageEmitterFunction = messageEmitterFunction; + this.pubnubInstance = pubnubInstance; + } + + public async override Task Run(EmitMessagesInvocation invocation) + { + var processedMessages = invocation.Messages?.Messages.Select(m => new PNMessageResult() + { + Channel = m.Channel, + Message = JsonConvert.DeserializeObject(m.Payload), + Subscription = m.SubscriptionMatch, + Timetoken = m.Timetoken.Timestamp, + UserMetadata = m.PublishMetadata, + Publisher = m.IssuingClientId + }); + + processedMessages?.ToList().ForEach(message => messageEmitterFunction(pubnubInstance, message)); + + } + + public override bool IsBackground(EmitMessagesInvocation invocation) => false; + + public override Task Cancel() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs new file mode 100644 index 000000000..1f207894d --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.Effects +{ + public class EmitStatusEffectHandler: EffectHandler + { + private readonly Action statusEmitterFunction; + private readonly Pubnub pubnubInstance; + + public EmitStatusEffectHandler(Pubnub pn, Action statusEmitter) + { + this.statusEmitterFunction = statusEmitter; + this.pubnubInstance = pn; + } + + public override Task Cancel() => Utils.EmptyTask; + + public override bool IsBackground(EmitStatusInvocation invocation) => false; + + public override async Task Run(EmitStatusInvocation invocation) + { + this.statusEmitterFunction(this.pubnubInstance, invocation.Status); + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs new file mode 100644 index 000000000..a517a0601 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using PubnubApi.EndPoint; +using PubnubApi.EventEngine.Common; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; + +namespace PubnubApi.EventEngine.Subscribe.Effects +{ + public class HandshakeEffectHandler : + EffectDoubleCancellableHandler + { + private SubscribeManager2 manager; + private EventQueue eventQueue; + + private Delay retryDelay = new Delay(0); + + internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) + { + this.manager = manager; + this.eventQueue = eventQueue; + } + + public override async Task Run(HandshakeReconnectInvocation invocation) + { + if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) + { + eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); + } + else + { + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); + await retryDelay.Start(); + if (!retryDelay.Cancelled) + await Run((HandshakeInvocation)invocation); + } + } + + public override bool IsBackground(HandshakeReconnectInvocation invocation) + { + return true; + } + + public override async Task Run(HandshakeInvocation invocation) + { + var response = await MakeHandshakeRequest(invocation); + SubscriptionCursor cursor = null; + if (response.Item1 != null) + { + cursor = new SubscriptionCursor() + { + Region = response.Item1.Timetoken.Region, + Timetoken = response.Item1.Timetoken.Timestamp + }; + } + + switch (invocation) + { + case Invocations.HandshakeReconnectInvocation reconnectInvocation when response.Item2.Error: + eventQueue.Enqueue(new Events.HandshakeReconnectFailureEvent() { AttemptedRetries = reconnectInvocation.AttemptedRetries + 1, Status = response.Item2}); + break; + case Invocations.HandshakeReconnectInvocation reconnectInvocation: + eventQueue.Enqueue(new Events.HandshakeReconnectSuccessEvent() { Cursor = cursor, Status = response.Item2 }); + break; + case { } when response.Item2.Error: + eventQueue.Enqueue(new Events.HandshakeFailureEvent() { Status = response.Item2}); + break; + case { }: + eventQueue.Enqueue(new Events.HandshakeSuccessEvent() { Cursor = cursor, Status = response.Item2 }); + break; + + } + } + + public override bool IsBackground(HandshakeInvocation invocation) + { + return false; + } + + + private async Task> MakeHandshakeRequest(HandshakeInvocation invocation) + { + return await manager.HandshakeRequest( + PNOperationType.PNSubscribeOperation, + invocation.Channels.ToArray(), + invocation.ChannelGroups.ToArray(), + null, + null, + invocation.InitialSubscribeQueryParams, + invocation.ExternalQueryParams + ); + } + + public override async Task Cancel() + { + if (!retryDelay.Cancelled) + { + retryDelay.Cancel(); + } + else + { + manager.HandshakeRequestCancellation(); + } + } + + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs new file mode 100644 index 000000000..b2aa6b3fe --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using PubnubApi.EndPoint; +using PubnubApi.EventEngine.Common; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; + +namespace PubnubApi.EventEngine.Subscribe.Effects +{ + public class ReceivingEffectHandler: + EffectDoubleCancellableHandler + { + private SubscribeManager2 manager; + private EventQueue eventQueue; + + private Delay retryDelay = new Delay(0); + + internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) + { + this.manager = manager; + this.eventQueue = eventQueue; + } + + public override Task Run(ReceiveReconnectInvocation invocation) + { + if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) + { + eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); + } + else + { + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); + // Run in the background + retryDelay.Start().ContinueWith((_) => this.Run((ReceiveMessagesInvocation)invocation)); + } + + return Utils.EmptyTask; + } + + public override bool IsBackground(ReceiveReconnectInvocation invocation) + { + return true; + } + + public override async Task Run(ReceiveMessagesInvocation invocation) + { + var response = await MakeReceiveMessagesRequest(invocation); + var cursor = new SubscriptionCursor() + { + Region = response.Item1?.Timetoken.Region, + Timetoken = response.Item1?.Timetoken.Timestamp + }; + + switch (invocation) + { + case Invocations.ReceiveReconnectInvocation reconnectInvocation when response.Item2.Error: + eventQueue.Enqueue(new Events.ReceiveReconnectFailureEvent() { AttemptedRetries = reconnectInvocation.AttemptedRetries + 1, Status = response.Item2}); + break; + case Invocations.ReceiveReconnectInvocation reconnectInvocation: + eventQueue.Enqueue(new Events.ReceiveReconnectSuccessEvent() { Cursor = cursor, Status = response.Item2 }); + break; + case { } when response.Item2.Error: + eventQueue.Enqueue(new Events.ReceiveFailureEvent() { Cursor = cursor, Status = response.Item2}); + break; + case { }: + eventQueue.Enqueue(new Events.ReceiveSuccessEvent() { Cursor = cursor, Messages= response.Item1, Status = response.Item2 }); + break; + } + } + + public override bool IsBackground(ReceiveMessagesInvocation invocation) + { + return true; + } + + private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) + { + return await manager.ReceiveRequest>( + PNOperationType.PNSubscribeOperation, + invocation.Channels?.ToArray(), + invocation.ChannelGroups?.ToArray(), + invocation.Cursor.Timetoken.Value, + invocation.Cursor.Region.Value, + invocation.InitialSubscribeQueryParams, + invocation.ExternalQueryParams + ); + } + + public override async Task Cancel() + { + if (!retryDelay.Cancelled) + { + retryDelay.Cancel(); + } + else + { + manager.ReceiveRequestCancellation(); + } + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs new file mode 100644 index 000000000..c8334f8de --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using PubnubApi.EventEngine.Subscribe.Common; + +namespace PubnubApi.EventEngine.Subscribe.Events { + public class UnsubscribeAllEvent : Core.IEvent { + } + + public class SubscriptionChangedEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + } + + public class SubscriptionRestoredEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + } + + public class HandshakeSuccessEvent : Core.IEvent { + public SubscriptionCursor Cursor; + public PNStatus Status; + } + + public class HandshakeFailureEvent : Core.IEvent { + public PNStatus Status; + public int AttemptedRetries; + } + + public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { + public PNStatus Status; + public SubscriptionCursor Cursor; + } + + public class HandshakeReconnectFailureEvent : HandshakeFailureEvent + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + } + + // Do we have this in system description ? + public class HandshakeReconnectRetryEvent : Core.IEvent { + } + + public class HandshakeReconnectGiveUpEvent : Core.IEvent { + public PNStatus Status; + } + + public class ReceiveSuccessEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public ReceivingResponse Messages; + public SubscriptionCursor Cursor; + public PNStatus Status; + } + + public class ReceiveFailureEvent : Core.IEvent { + public PNStatus Status; + public int AttemptedRetries; + public SubscriptionCursor Cursor; + } + + public class ReceiveReconnectRetry : Core.IEvent { + } + + public class ReceiveReconnectSuccessEvent : ReceiveSuccessEvent { + } + + public class ReceiveReconnectFailureEvent : ReceiveFailureEvent { + } + + public class ReceiveReconnectGiveUpEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + public PNStatus Status; + } + + public class DisconnectEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + } + + public class ReconnectEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs new file mode 100644 index 000000000..c4e4773bc --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; + +namespace PubnubApi.EventEngine.Subscribe.Invocations { + public class EmitMessagesInvocation : Core.IEffectInvocation { + public ReceivingResponse Messages; + + public EmitMessagesInvocation(ReceivingResponse messages) + { + this.Messages = messages; + } + } + + public class EmitStatusInvocation : Core.IEffectInvocation { + // TODO merge status variables into one? + public PNStatusCategory StatusCategory; + public PNStatus Status; + + public EmitStatusInvocation(PNStatus status) + { + this.Status = status; + if (status != null) + { + this.StatusCategory = status.Category; + } + } + + public EmitStatusInvocation(PNStatusCategory category) + { + this.StatusCategory = category; + this.Status = new PNStatus() + { + Category = category, + }; + } + } + + public class HandshakeInvocation : Core.IEffectInvocation { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + // TODO if we need these, figure out how to pass them. + public Dictionary InitialSubscribeQueryParams = new Dictionary(); + public Dictionary ExternalQueryParams = new Dictionary(); + } + + public class ReceiveMessagesInvocation : Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + public Dictionary InitialSubscribeQueryParams = new Dictionary(); + public Dictionary ExternalQueryParams = new Dictionary(); + } + + public class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } + + public class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } + + public class HandshakeReconnectInvocation: HandshakeInvocation + { + public ReconnectionConfiguration ReconnectionConfiguration; + public int AttemptedRetries; + } + + public class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } + + public class ReceiveReconnectInvocation: ReceiveMessagesInvocation + { + public ReconnectionConfiguration ReconnectionConfiguration; + public int AttemptedRetries; + } + + public class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs new file mode 100644 index 000000000..f01083e28 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class HandshakeFailedState : SubscriptionState + { + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReconnectEvent reconnect => new HandshakingState() + { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs new file mode 100644 index 000000000..eae980448 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class HandshakeReconnectingState : SubscriptionState + { + public int AttemptedRetries; + + public override IEnumerable OnEntry => new HandshakeReconnectInvocation() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = this.AttemptedRetries + }.AsArray(); + public override IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.DisconnectEvent disconnect => new HandshakeStoppedState() + { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUp => new HandshakeFailedState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With( + new EmitStatusInvocation(handshakeReconnectGiveUp.Status) + ), + + Events.HandshakeReconnectFailureEvent handshakeReconnectFailure => new HandshakeReconnectingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = this.AttemptedRetries + 1 + }.With(new EmitStatusInvocation(handshakeReconnectFailure.Status)), + + Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess => new ReceivingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = handshakeReconnectSuccess.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(handshakeReconnectSuccess.Status)), + + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs new file mode 100644 index 000000000..db3455832 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class HandshakeStoppedState : SubscriptionState + { + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReconnectEvent reconnect => new HandshakingState() + { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs new file mode 100644 index 000000000..457f9316a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class HandshakingState : SubscriptionState + { + public SubscriptionCursor Cursor { get; set; } + public override IEnumerable OnEntry => new HandshakeInvocation() + { Channels = this.Channels, + ChannelGroups = this.ChannelGroups }.AsArray(); + + public override IEnumerable OnExit { get; } = new CancelHandshakeInvocation().AsArray(); + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new States.HandshakingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new States.HandshakingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeReconnectingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = 0 + }.With(new EmitStatusInvocation(handshakeFailure.Status)), + + Events.DisconnectEvent disconnect => new States.HandshakeStoppedState() + { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), + + Events.HandshakeSuccessEvent success => new ReceivingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = success.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(success.Status)), + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs new file mode 100644 index 000000000..9fb34c77f --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class ReceiveFailedState : SubscriptionState + { + public SubscriptionCursor Cursor; + + public override IEnumerable OnEntry { get; } + public override IEnumerable OnExit { get; } + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReconnectEvent reconnect => new HandshakingState() + { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs new file mode 100644 index 000000000..61dcbf397 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class ReceiveReconnectingState : SubscriptionState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + public ReconnectionConfiguration ReconnectionConfiguration; + public int AttemptedRetries; + + public override IEnumerable OnEntry => new ReceiveReconnectInvocation() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = this.AttemptedRetries + }.AsArray(); + + public override IEnumerable OnExit { get; } = + new CancelReceiveReconnectInvocation().AsArray(); + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.DisconnectEvent disconnect => new ReceiveStoppedState() + { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + Cursor = disconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReceiveReconnectSuccessEvent receiveReconnectSuccess => new ReceivingState() + { + Channels = receiveReconnectSuccess.Channels, + ChannelGroups = receiveReconnectSuccess.ChannelGroups, + Cursor = receiveReconnectSuccess.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(receiveReconnectSuccess.Status)), + + Events.ReceiveReconnectFailureEvent receiveReconnectFailure => new ReceiveReconnectingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = this.AttemptedRetries + 1 + }.With(new EmitStatusInvocation(receiveReconnectFailure.Status)), + + Events.ReceiveReconnectGiveUpEvent receiveReconnectGiveUp => new ReceiveFailedState() + { + Channels = receiveReconnectGiveUp.Channels, + ChannelGroups = receiveReconnectGiveUp.ChannelGroups, + Cursor = receiveReconnectGiveUp.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(receiveReconnectGiveUp.Status)), + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs new file mode 100644 index 000000000..93c5236bd --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class ReceiveStoppedState : SubscriptionState + { + public SubscriptionCursor Cursor; + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionChangedEvent subscriptionChanged => new ReceiveStoppedState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReconnectEvent reconnect => new HandshakingState() + { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceiveStoppedState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs new file mode 100644 index 000000000..010541e09 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class ReceivingState : SubscriptionState + { + public SubscriptionCursor Cursor; + + public override IEnumerable OnEntry => new ReceiveMessagesInvocation() + { Channels = this.Channels,ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); + + public override IEnumerable OnExit { get; } = new CancelReceiveMessagesInvocation().AsArray(); + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReceiveSuccessEvent receiveSuccess => new ReceivingState() + { + Channels = receiveSuccess.Channels, + ChannelGroups = receiveSuccess.ChannelGroups, + Cursor = receiveSuccess.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With( + new EmitMessagesInvocation(receiveSuccess.Messages), + new EmitStatusInvocation(receiveSuccess.Status) + ), + + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.DisconnectEvent disconnect => new ReceiveStoppedState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), + + Events.ReceiveFailureEvent receiveFailure => new ReceiveReconnectingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = 0 + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs new file mode 100644 index 000000000..132a0a06d --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.States +{ + public class UnsubscribedState : SubscriptionState + { + public override TransitionResult Transition(Core.IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs new file mode 100644 index 000000000..da88ecc14 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -0,0 +1,51 @@ +using PubnubApi.EndPoint; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.States; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Effects; +using PubnubApi.EventEngine.Subscribe.Invocations; +using System; + +namespace PubnubApi.EventEngine.Subscribe { + public class SubscribeEventEngine : Engine { + private SubscribeManager2 subscribeManager; + + internal SubscribeEventEngine(Pubnub pubnubInstance, + PNConfiguration pubnubConfiguration, + SubscribeManager2 subscribeManager, + Action statusListener = null, + Action> messageListener = null) + { + this.subscribeManager = subscribeManager; + + // initialize the handler, pass dependencies + var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, EventQueue); + dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); + + var receiveHandler = new Effects.ReceivingEffectHandler(subscribeManager, EventQueue); + dispatcher.Register(receiveHandler); + dispatcher.Register(receiveHandler); + dispatcher.Register(receiveHandler); + dispatcher.Register(receiveHandler); + + var emitMessageHandler = new Effects.EmitMessagesHandler(pubnubInstance, messageListener); + dispatcher.Register(emitMessageHandler); + + var emitStatusHandler = new Effects.EmitStatusEffectHandler(pubnubInstance, statusListener); + dispatcher.Register(emitStatusHandler); + + currentState = new UnsubscribedState() { ReconnectionConfiguration = new Context.ReconnectionConfiguration(pubnubConfiguration.ReconnectionPolicy, pubnubConfiguration.ConnectionMaxRetries) }; + } + public void Subscribe(string[] channels, string[] channelGroups) + { + this.EventQueue.Enqueue(new SubscriptionChangedEvent() { Channels = channels, ChannelGroups = channelGroups }); + } + public void UnsubscribeAll() + { + this.EventQueue.Enqueue(new UnsubscribeAllEvent()); + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs new file mode 100644 index 000000000..723fbcb40 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Concurrent; +using PubnubApi.EndPoint; + +namespace PubnubApi.EventEngine.Subscribe +{ + public class SubscribeEventEngineFactory + { + private ConcurrentDictionary engineinstances; + internal SubscribeEventEngineFactory() + { + this.engineinstances = new ConcurrentDictionary(); + } + internal bool hasEventEngine(string instanceId) + { + return engineinstances.ContainsKey(instanceId); + } + internal SubscribeEventEngine getEventEngine(string instanceId) + { + SubscribeEventEngine subscribeEventEngine; + engineinstances.TryGetValue(instanceId, out subscribeEventEngine); + return subscribeEventEngine; + } + + internal SubscribeEventEngine initializeEventEngine(string instanceId, + Pubnub pubnubInstance, + PNConfiguration pubnubConfiguration, + SubscribeManager2 subscribeManager, + Action statusListener = null, + Action> messageListener= null) + { + var subscribeEventEngine = new SubscribeEventEngine(pubnubInstance, pubnubConfiguration: pubnubConfiguration, subscribeManager,statusListener, null); //TODO: replace with message listener + if (engineinstances.TryAdd(instanceId, subscribeEventEngine)) { + return subscribeEventEngine; + } + else { + throw new Exception("Subscribe event engine initialisation failed!"); + } + + } + } +} + diff --git a/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs b/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs index d42436475..fd5de104e 100644 --- a/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs +++ b/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs @@ -1,4 +1,5 @@ -using System; +using PubnubApi.PubnubEventEngine; +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -49,5 +50,18 @@ bool IncludeUuid get; set; } + + List> EventTypeList + { + get; + set; + } + + int Attempts + { + get; + set; + } + } } diff --git a/src/Api/PubnubApi/Interface/ISubscribeOperation.cs b/src/Api/PubnubApi/Interface/ISubscribeOperation.cs new file mode 100644 index 000000000..63451361d --- /dev/null +++ b/src/Api/PubnubApi/Interface/ISubscribeOperation.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi +{ + public interface ISubscribeOperation + { + List SubscribeListenerList { get; set;} + ISubscribeOperation Channels(string[] channels); + ISubscribeOperation ChannelGroups(string[] channelGroups); + ISubscribeOperation WithTimetoken(long timetoken); + ISubscribeOperation WithPresence(); + ISubscribeOperation QueryParam(Dictionary customQueryParam); + void Execute(); + } +} diff --git a/src/Api/PubnubApi/Interface/IUrlRequestBuilder.cs b/src/Api/PubnubApi/Interface/IUrlRequestBuilder.cs index 2d3f7a5b8..687173a89 100644 --- a/src/Api/PubnubApi/Interface/IUrlRequestBuilder.cs +++ b/src/Api/PubnubApi/Interface/IUrlRequestBuilder.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace PubnubApi.Interface +namespace PubnubApi { public interface IUrlRequestBuilder { diff --git a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs index 5abb9de27..2c0d34eb3 100644 --- a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs +++ b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs @@ -14,6 +14,20 @@ public class PNStatus public PNStatus() { } + public PNStatus(Exception e, PNOperationType operationType, PNStatusCategory category, IEnumerable affectedChannels = null, IEnumerable affectedChannelGroups = null) + { + this.Error = e != null; + this.Operation = operationType; + this.ErrorData = new PNErrorData(e?.Message, e); + this.AffectedChannels = affectedChannels?.ToList(); + this.AffectedChannelGroups = affectedChannelGroups?.ToList(); + this.Category = category; + if (!Error) + { + this.StatusCode = 200; + } + } + internal PNStatus(object endpointOperation) { this.savedEndpointOperation = endpointOperation; diff --git a/src/Api/PubnubApi/PNConfiguration.cs b/src/Api/PubnubApi/PNConfiguration.cs index 6171c9cbb..05d126c1e 100644 --- a/src/Api/PubnubApi/PNConfiguration.cs +++ b/src/Api/PubnubApi/PNConfiguration.cs @@ -141,7 +141,13 @@ public UserId UserId public bool SuppressLeaveEvents { get; set; } + public bool UseRandomInitializationVector { get; set; } + + public bool MaintainPresenceState { get; set; } = true; + + public bool EnableEventEngine { get; set; } public int FileMessagePublishRetryLimit { get; set; } + public int ConnectionMaxRetries { get; set; } [Obsolete("PNConfiguration(string uuid) is deprecated, please use PNConfiguration(UserId userId) instead.")] public PNConfiguration(string uuid) @@ -186,6 +192,8 @@ private void ConstructorInit(UserId currentUserId) UseRandomInitializationVector = true; FileMessagePublishRetryLimit = 5; _userId = currentUserId; + EnableEventEngine = false; + ConnectionMaxRetries = -1; } public PNConfiguration SetPresenceTimeoutWithCustomInterval(int timeout, int interval) { diff --git a/src/Api/PubnubApi/Pubnub.cs b/src/Api/PubnubApi/Pubnub.cs index 1c326c7b6..89ba1e252 100644 --- a/src/Api/PubnubApi/Pubnub.cs +++ b/src/Api/PubnubApi/Pubnub.cs @@ -3,6 +3,11 @@ using System.Globalization; using System.Reflection; using PubnubApi.EndPoint; +using PubnubApi.EventEngine.Subscribe; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.Interface; +using PubnubApi.EventEngine.Presence; #if !NET35 && !NET40 using System.Collections.Concurrent; #endif @@ -21,6 +26,12 @@ public class Pubnub private readonly EndPoint.TokenManager tokenManager; private object savedSubscribeOperation; private readonly string savedSdkVerion; + private SubscribeEventEngineFactory subscribeEventEngineFactory; + private List subscribeCallbackListenerList + { + get; + set; + } = new List(); #if UNITY private static System.Func> OnCleanupCall; @@ -50,12 +61,24 @@ public static void CleanUp() #region "PubNub API Channel Methods" - public EndPoint.SubscribeOperation Subscribe() + public ISubscribeOperation Subscribe() { - EndPoint.SubscribeOperation subscribeOperation = new EndPoint.SubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); - subscribeOperation.CurrentPubnubInstance(this); - savedSubscribeOperation = subscribeOperation; - return subscribeOperation; + if (pubnubConfig[InstanceId].EnableEventEngine) + { + EndPoint.SubscribeOperation2 subscribeOperation = new EndPoint.SubscribeOperation2(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this.subscribeEventEngineFactory,InstanceId ,this); + subscribeOperation.SubscribeListenerList = subscribeCallbackListenerList; + + //subscribeOperation.CurrentPubnubInstance(this); + savedSubscribeOperation = subscribeOperation; + return subscribeOperation; + } + else + { + EndPoint.SubscribeOperation subscribeOperation = new EndPoint.SubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); + //subscribeOperation.CurrentPubnubInstance(this); + savedSubscribeOperation = subscribeOperation; + return subscribeOperation; + } } public EndPoint.UnsubscribeOperation Unsubscribe() @@ -355,19 +378,27 @@ public EndPoint.ListAllChannelGroupOperation ListChannelGroups() public bool AddListener(SubscribeCallback listener) { - if (listenerManager == null) + if (pubnubConfig[InstanceId].EnableEventEngine) { - listenerManager = new EndPoint.ListenerManager(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); + subscribeCallbackListenerList.Add(listener); + return true; + } + else + { + if (listenerManager == null) + { + listenerManager = new EndPoint.ListenerManager(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); + } + return listenerManager.AddListener(listener); } - return listenerManager.AddListener(listener); } public bool RemoveListener(SubscribeCallback listener) { bool ret = false; - if (listenerManager != null) + if (subscribeCallbackListenerList != null) { - ret = listenerManager.RemoveListener(listener); + ret = subscribeCallbackListenerList.Remove(listener); } return ret; } @@ -872,6 +903,7 @@ public Pubnub(PNConfiguration config) pubnubLog = config.PubnubLog; savedSdkVerion = Version; InstanceId = Guid.NewGuid().ToString(); + subscribeEventEngineFactory = new SubscribeEventEngineFactory(); pubnubConfig.AddOrUpdate(InstanceId, config, (k, o) => config); if (pubnubLog != null) { diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index 8d2f0e746..eca70a1d4 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -70,8 +70,8 @@ - - 0.2.0 + + 0.3.1 None @@ -89,7 +89,8 @@ - + + 0.3.1 None diff --git a/src/Api/PubnubApi/PubnubCoreBase.cs b/src/Api/PubnubApi/PubnubCoreBase.cs index df4f6ca6f..5e9526d1d 100644 --- a/src/Api/PubnubApi/PubnubCoreBase.cs +++ b/src/Api/PubnubApi/PubnubCoreBase.cs @@ -834,7 +834,7 @@ private void ResponseToUserCallback(List result, PNOperationType type if (fileObjDic != null && fileObjDic.ContainsKey("id") && fileObjDic.ContainsKey("name")) { fileMessage.File = new PNFile { Id = fileObjDic["id"].ToString(), Name = fileObjDic["name"].ToString() }; - PubnubApi.Interface.IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(currentConfig, jsonLib, unitTest, currentLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(currentConfig, jsonLib, unitTest, currentLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); Uri fileUrlRequest = urlBuilder.BuildGetFileUrlOrDeleteReqest("GET", "", fileMessage.Channel, fileMessage.File.Id, fileMessage.File.Name, null, type); fileMessage.File.Url = fileUrlRequest.ToString(); } diff --git a/src/Api/PubnubApi/PubnubHttp.cs b/src/Api/PubnubApi/PubnubHttp.cs index 4f0d7aeb5..818e23166 100644 --- a/src/Api/PubnubApi/PubnubHttp.cs +++ b/src/Api/PubnubApi/PubnubHttp.cs @@ -610,7 +610,7 @@ async Task SendRequestAndGetJsonResponseTaskFactory(RequestState p System.Diagnostics.Debug.WriteLine(jsonString); pubnubRequestState.GotJsonResponse = true; System.Diagnostics.Debug.WriteLine(""); - System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Retrieved JSON", DateTime.Now.ToString(CultureInfo.InvariantCulture))); + System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SendRequestAndGetJsonResponseTaskFactory => Retrieved JSON", DateTime.Now.ToString(CultureInfo.InvariantCulture))); if (pubnubRequestState.Response != null) { @@ -965,7 +965,7 @@ async Task SendRequestAndGetJsonResponseClassicHttp(Uri requestUri, R System.Diagnostics.Debug.WriteLine(jsonString); System.Diagnostics.Debug.WriteLine(""); - System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Retrieved JSON", DateTime.Now.ToString(CultureInfo.InvariantCulture))); + System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SendRequestAndGetJsonResponseClassicHttp => Retrieved JSON", DateTime.Now.ToString(CultureInfo.InvariantCulture))); taskComplete.TrySetResult(jsonString); } if (asyncRequestState.Response != null) @@ -1052,7 +1052,7 @@ async Task SendRequestAndGetStreamResponseClassicHttp(RequestState System.Diagnostics.Debug.WriteLine(jsonString); System.Diagnostics.Debug.WriteLine(""); - System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Retrieved JSON", DateTime.Now.ToString(CultureInfo.InvariantCulture))); + System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SendRequestAndGetStreamResponseClassicHttp => Retrieved JSON", DateTime.Now.ToString(CultureInfo.InvariantCulture))); taskComplete.TrySetResult(null); } if (asyncRequestState.Response != null) diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 936976912..6f8bd20cb 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -149,9 +149,11 @@ EndPoint\PubSub\SubscribeManager.cs + EndPoint\PubSub\SubscribeOperation.cs + EndPoint\PubSub\UnsubscribeAllOperation.cs @@ -211,6 +213,42 @@ Enum\ResponseType.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HttpUtility\HttpUtility.cs @@ -233,6 +271,7 @@ Interface\IPubnubUnitTest.cs + Interface\IUrlRequestBuilder.cs @@ -893,6 +932,14 @@ + + + + + + + + diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index c24bc1fc2..3b0bbeec7 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -262,9 +262,15 @@ EndPoint\PubSub\SubscribeManager.cs + + EndPoint\PubSub\SubscribeManager2.cs + EndPoint\PubSub\SubscribeOperation.cs + + EndPoint\PubSub\SubscribeOperation2.cs + EndPoint\PubSub\UnsubscribeAllOperation.cs @@ -324,6 +330,42 @@ Enum\ResponseType.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HttpUtility\HttpUtility.cs @@ -340,6 +382,9 @@ Interface\IPubnubHttp.cs + + Interface\ISubscribeOperation.cs + Interface\IPubnubLog.cs @@ -693,7 +738,7 @@ --> - 6.2.9 + 6.2.14 13.0.1 @@ -712,6 +757,14 @@ + + + + + + + + diff --git a/src/Pubnub.sln b/src/Pubnub.sln index ee8f238b4..5b28d1882 100644 --- a/src/Pubnub.sln +++ b/src/Pubnub.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29418.71 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33213.308 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Api", "Api", "{F101EAB9-89CA-4ACB-A72B-0660F4C9CC38}" EndProject @@ -27,7 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PubnubApiPCL.Tests", "UnitT EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MockServerPubnubApiPCL", "UnitTests\MockServerPubnubApiPCL\MockServerPubnubApiPCL.csproj", "{C1449A27-2C29-40FF-BAD4-521BDFD323EB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AcceptanceTests", "UnitTests\AcceptanceTests\AcceptanceTests.csproj", "{15805B6C-C474-4DD7-BD7F-150A7EA23F5C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AcceptanceTests", "UnitTests\AcceptanceTests\AcceptanceTests.csproj", "{15805B6C-C474-4DD7-BD7F-150A7EA23F5C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PubnubApiUnity", "Api\PubnubApiUnity\PubnubApiUnity.csproj", "{55D8FD89-D9CF-4A11-B997-948AFF801A43}" EndProject @@ -35,18 +35,22 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_Ubuntu|Any CPU = Debug_Ubuntu|Any CPU Debug_Ubuntu|ARM = Debug_Ubuntu|ARM + Debug_Ubuntu|ARM64 = Debug_Ubuntu|ARM64 Debug_Ubuntu|x64 = Debug_Ubuntu|x64 Debug_Ubuntu|x86 = Debug_Ubuntu|x86 Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release_Ubuntu|Any CPU = Release_Ubuntu|Any CPU Release_Ubuntu|ARM = Release_Ubuntu|ARM + Release_Ubuntu|ARM64 = Release_Ubuntu|ARM64 Release_Ubuntu|x64 = Release_Ubuntu|x64 Release_Ubuntu|x86 = Release_Ubuntu|x86 Release|Any CPU = Release|Any CPU Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection @@ -55,6 +59,8 @@ Global {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -63,6 +69,8 @@ Global {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|Any CPU.Build.0 = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|ARM.ActiveCfg = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|ARM.Build.0 = Debug|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|ARM64.Build.0 = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|x64.ActiveCfg = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|x64.Build.0 = Debug|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -71,6 +79,8 @@ Global {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -79,6 +89,8 @@ Global {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|Any CPU.Build.0 = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|ARM.ActiveCfg = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|ARM.Build.0 = Release|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|ARM64.ActiveCfg = Release|Any CPU + {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|ARM64.Build.0 = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|x64.ActiveCfg = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|x64.Build.0 = Release|Any CPU {B23455AF-6376-482C-BBED-74C3744D6AA6}.Release|x86.ActiveCfg = Release|Any CPU @@ -87,6 +99,8 @@ Global {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -95,6 +109,8 @@ Global {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|Any CPU.Build.0 = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|ARM.ActiveCfg = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|ARM.Build.0 = Debug|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|ARM64.Build.0 = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|x64.ActiveCfg = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|x64.Build.0 = Debug|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -103,6 +119,8 @@ Global {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -111,6 +129,8 @@ Global {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|Any CPU.Build.0 = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|ARM.ActiveCfg = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|ARM.Build.0 = Release|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|ARM64.ActiveCfg = Release|Any CPU + {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|ARM64.Build.0 = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|x64.ActiveCfg = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|x64.Build.0 = Release|Any CPU {07F3E54E-5BE0-4857-9B46-2A0C062A2D49}.Release|x86.ActiveCfg = Release|Any CPU @@ -119,6 +139,8 @@ Global {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -127,6 +149,8 @@ Global {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|Any CPU.Build.0 = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|ARM.ActiveCfg = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|ARM.Build.0 = Debug|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|ARM64.Build.0 = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|x64.ActiveCfg = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|x64.Build.0 = Debug|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -135,6 +159,8 @@ Global {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -143,6 +169,8 @@ Global {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|Any CPU.Build.0 = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|ARM.ActiveCfg = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|ARM.Build.0 = Release|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|ARM64.ActiveCfg = Release|Any CPU + {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|ARM64.Build.0 = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|x64.ActiveCfg = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|x64.Build.0 = Release|Any CPU {FE12044C-3BA8-4ABE-A954-779F58F08A14}.Release|x86.ActiveCfg = Release|Any CPU @@ -151,6 +179,8 @@ Global {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -159,6 +189,8 @@ Global {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|ARM.ActiveCfg = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|ARM.Build.0 = Debug|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|ARM64.Build.0 = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|x64.ActiveCfg = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|x64.Build.0 = Debug|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -167,6 +199,8 @@ Global {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -175,6 +209,8 @@ Global {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|Any CPU.Build.0 = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|ARM.ActiveCfg = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|ARM.Build.0 = Release|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|ARM64.ActiveCfg = Release|Any CPU + {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|ARM64.Build.0 = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|x64.ActiveCfg = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|x64.Build.0 = Release|Any CPU {CF16D5E8-923F-465B-A510-A3E851BDD642}.Release|x86.ActiveCfg = Release|Any CPU @@ -183,6 +219,8 @@ Global {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -191,6 +229,8 @@ Global {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|Any CPU.Build.0 = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|ARM.ActiveCfg = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|ARM.Build.0 = Debug|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|ARM64.Build.0 = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|x64.ActiveCfg = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|x64.Build.0 = Debug|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -199,6 +239,8 @@ Global {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -207,6 +249,8 @@ Global {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|Any CPU.Build.0 = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|ARM.ActiveCfg = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|ARM.Build.0 = Release|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|ARM64.ActiveCfg = Release|Any CPU + {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|ARM64.Build.0 = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|x64.ActiveCfg = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|x64.Build.0 = Release|Any CPU {B86ACD44-B255-4BB1-90D0-6C4751B51D7F}.Release|x86.ActiveCfg = Release|Any CPU @@ -215,6 +259,8 @@ Global {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -223,6 +269,8 @@ Global {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|Any CPU.Build.0 = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|ARM.ActiveCfg = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|ARM.Build.0 = Debug|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|ARM64.Build.0 = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|x64.ActiveCfg = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|x64.Build.0 = Debug|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -231,6 +279,8 @@ Global {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -239,6 +289,8 @@ Global {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|Any CPU.Build.0 = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|ARM.ActiveCfg = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|ARM.Build.0 = Release|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|ARM64.ActiveCfg = Release|Any CPU + {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|ARM64.Build.0 = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|x64.ActiveCfg = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|x64.Build.0 = Release|Any CPU {E71580C3-AF9E-4F06-A015-27D05F129D09}.Release|x86.ActiveCfg = Release|Any CPU @@ -247,6 +299,8 @@ Global {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -255,6 +309,8 @@ Global {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|ARM.ActiveCfg = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|ARM.Build.0 = Debug|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|ARM64.Build.0 = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|x64.ActiveCfg = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|x64.Build.0 = Debug|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -263,6 +319,8 @@ Global {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -271,6 +329,8 @@ Global {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|Any CPU.Build.0 = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|ARM.ActiveCfg = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|ARM.Build.0 = Release|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|ARM64.ActiveCfg = Release|Any CPU + {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|ARM64.Build.0 = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|x64.ActiveCfg = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|x64.Build.0 = Release|Any CPU {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C}.Release|x86.ActiveCfg = Release|Any CPU @@ -279,6 +339,8 @@ Global {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -287,6 +349,8 @@ Global {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|ARM.ActiveCfg = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|ARM.Build.0 = Debug|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|ARM64.Build.0 = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|x64.ActiveCfg = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|x64.Build.0 = Debug|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -295,6 +359,8 @@ Global {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -303,6 +369,8 @@ Global {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|Any CPU.Build.0 = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|ARM.ActiveCfg = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|ARM.Build.0 = Release|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|ARM64.ActiveCfg = Release|Any CPU + {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|ARM64.Build.0 = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|x64.ActiveCfg = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|x64.Build.0 = Release|Any CPU {C1449A27-2C29-40FF-BAD4-521BDFD323EB}.Release|x86.ActiveCfg = Release|Any CPU @@ -311,6 +379,8 @@ Global {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU @@ -319,6 +389,8 @@ Global {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|Any CPU.Build.0 = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|ARM.ActiveCfg = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|ARM.Build.0 = Debug|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|ARM64.Build.0 = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|x64.ActiveCfg = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|x64.Build.0 = Debug|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -327,6 +399,8 @@ Global {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|x64.Build.0 = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU @@ -335,6 +409,8 @@ Global {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|Any CPU.Build.0 = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|ARM.ActiveCfg = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|ARM.Build.0 = Release|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|ARM64.ActiveCfg = Release|Any CPU + {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|ARM64.Build.0 = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|x64.ActiveCfg = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|x64.Build.0 = Release|Any CPU {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|x86.ActiveCfg = Release|Any CPU diff --git a/src/UnitTests/AcceptanceTests/AcceptanceTests.csproj b/src/UnitTests/AcceptanceTests/AcceptanceTests.csproj index 41570f3a5..ee614cbdf 100644 --- a/src/UnitTests/AcceptanceTests/AcceptanceTests.csproj +++ b/src/UnitTests/AcceptanceTests/AcceptanceTests.csproj @@ -9,23 +9,23 @@ - - + + - - - + + + - + diff --git a/src/UnitTests/AcceptanceTests/Features/channel-metadata.feature.cs b/src/UnitTests/AcceptanceTests/Features/channel-metadata.feature.cs index 93206af86..fd47689f4 100644 --- a/src/UnitTests/AcceptanceTests/Features/channel-metadata.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/channel-metadata.feature.cs @@ -28,7 +28,7 @@ public partial class ObjectsV2ChannelMetadataFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=objectsV2", "beta"}; @@ -39,9 +39,7 @@ public partial class ObjectsV2ChannelMetadataFeature public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 Channel metadata", " As a PubNub customer I want to create, update, remove channels.", ProgrammingLanguage.CSharp, new string[] { - "featureSet=objectsV2", - "beta"}); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 Channel metadata", " As a PubNub customer I want to create, update, remove channels.", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } @@ -53,28 +51,28 @@ public virtual void FeatureTearDown() } [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() + public void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] - public virtual void TestTearDown() + public void TestTearDown() { testRunner.OnScenarioEnd(); } - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioInitialize(scenarioInfo); testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public virtual void ScenarioStart() + public void ScenarioStart() { testRunner.OnScenarioStart(); } - public virtual void ScenarioCleanup() + public void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } @@ -91,26 +89,16 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get a channel metadata for id")] [NUnit.Framework.CategoryAttribute("contract=getChannelMetadataOfChat")] - public virtual void GetAChannelMetadataForId() + public void GetAChannelMetadataForId() { string[] tagsOfScenario = new string[] { "contract=getChannelMetadataOfChat"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a channel metadata for id", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a channel metadata for id", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 9 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -139,26 +127,16 @@ public virtual void GetAChannelMetadataForId() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get a channel with custom metadata")] [NUnit.Framework.CategoryAttribute("contract=getChannelMetadataOfDMWithCustom")] - public virtual void GetAChannelWithCustomMetadata() + public void GetAChannelWithCustomMetadata() { string[] tagsOfScenario = new string[] { "contract=getChannelMetadataOfDMWithCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a channel with custom metadata", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a channel with custom metadata", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 16 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -187,26 +165,16 @@ public virtual void GetAChannelWithCustomMetadata() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Set a channel metadata")] [NUnit.Framework.CategoryAttribute("contract=setChannelMetadataForChat")] - public virtual void SetAChannelMetadata() + public void SetAChannelMetadata() { string[] tagsOfScenario = new string[] { "contract=setChannelMetadataForChat"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set a channel metadata", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set a channel metadata", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 23 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -235,26 +203,16 @@ public virtual void SetAChannelMetadata() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Remove a channel metadata for id")] [NUnit.Framework.CategoryAttribute("contract=removeChannelMetadataOfChat")] - public virtual void RemoveAChannelMetadataForId() + public void RemoveAChannelMetadataForId() { string[] tagsOfScenario = new string[] { "contract=removeChannelMetadataOfChat"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove a channel metadata for id", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove a channel metadata for id", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 30 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -280,26 +238,16 @@ public virtual void RemoveAChannelMetadataForId() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get all channel metadata")] [NUnit.Framework.CategoryAttribute("contract=getAllChannelMetadata")] - public virtual void GetAllChannelMetadata() + public void GetAllChannelMetadata() { string[] tagsOfScenario = new string[] { "contract=getAllChannelMetadata"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all channel metadata", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all channel metadata", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 36 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -325,26 +273,16 @@ public virtual void GetAllChannelMetadata() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get all channel metadata with custom")] [NUnit.Framework.CategoryAttribute("contract=getAllChannelMetadataWithCustom")] - public virtual void GetAllChannelMetadataWithCustom() + public void GetAllChannelMetadataWithCustom() { string[] tagsOfScenario = new string[] { "contract=getAllChannelMetadataWithCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all channel metadata with custom", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all channel metadata with custom", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 42 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } diff --git a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature new file mode 100644 index 000000000..386935a3b --- /dev/null +++ b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature @@ -0,0 +1,94 @@ +@featureSet=eventEngine +Feature: Event Engine + This is a description of the feature + + Background: + Given the demo keyset with event engine enabled + + @contract=simpleSubscribe @beta + Scenario: Successfully receive messages + When I subscribe + When I publish a message + Then I receive the message in my subscribe response + And I observe the following: + | type | name | + | event | SUBSCRIPTION_CHANGED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_SUCCESS | + | invocation | CANCEL_HANDSHAKE | + | invocation | EMIT_STATUS | + | invocation | RECEIVE_EVENTS | + | event | RECEIVE_SUCCESS | + | invocation | CANCEL_RECEIVE_EVENTS | + | invocation | EMIT_STATUS | + | invocation | EMIT_EVENTS | + + @contract=subscribeHandshakeFailure @beta + Scenario: Complete handshake failure + Given a linear reconnection policy with 3 retries + When I subscribe + Then I receive an error + And I observe the following: + | type | name | + | event | SUBSCRIPTION_CHANGED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_FAILURE | + | invocation | CANCEL_HANDSHAKE | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_FAILURE | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_FAILURE | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_FAILURE | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_GIVEUP | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | EMIT_STATUS | + + @contract=subscribeHandshakeRecovery @beta + Scenario: Handshake failure recovery + Given a linear reconnection policy with 3 retries + When I subscribe + Then I receive the message in my subscribe response + And I observe the following: + | type | name | + | event | SUBSCRIPTION_CHANGED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_FAILURE | + | invocation | CANCEL_HANDSHAKE | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_FAILURE | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_SUCCESS | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | EMIT_STATUS | + | invocation | RECEIVE_EVENTS | + | event | RECEIVE_SUCCESS | + | invocation | CANCEL_RECEIVE_EVENTS | + | invocation | EMIT_STATUS | + | invocation | EMIT_EVENTS | + + @contract=subscribeReceivingRecovery @beta + Scenario: Receiving failure recovery + When I subscribe + Then I receive the message in my subscribe response + And I observe the following: + | type | name | + | event | SUBSCRIPTION_CHANGED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_SUCCESS | + | invocation | CANCEL_HANDSHAKE | + | invocation | EMIT_STATUS | + | invocation | RECEIVE_EVENTS | + | event | RECEIVE_FAILURE | + | invocation | CANCEL_RECEIVE_EVENTS | + | invocation | RECEIVE_RECONNECT | + | event | RECEIVE_RECONNECT_SUCCESS | + | invocation | CANCEL_RECEIVE_RECONNECT | + | invocation | EMIT_STATUS | + | invocation | EMIT_EVENTS | + | invocation | RECEIVE_EVENTS | \ No newline at end of file diff --git a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs new file mode 100644 index 000000000..841b19aba --- /dev/null +++ b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs @@ -0,0 +1,429 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by SpecFlow (https://www.specflow.org/). +// SpecFlow Version:3.9.0.0 +// SpecFlow Generator Version:3.9.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +namespace AcceptanceTests.Features.Event_Engine +{ + using TechTalk.SpecFlow; + using System; + using System.Linq; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.9.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("Event Engine")] + [NUnit.Framework.CategoryAttribute("featureSet=eventEngine")] + public partial class EventEngineFeature + { + + private TechTalk.SpecFlow.ITestRunner testRunner; + + private static string[] featureTags = new string[] { + "featureSet=eventEngine"}; + +#line 1 "happy-path.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public virtual void FeatureSetup() + { + testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features/event-engine", "Event Engine", " This is a description of the feature", ProgrammingLanguage.CSharp, featureTags); + testRunner.OnFeatureStart(featureInfo); + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public virtual void FeatureTearDown() + { + testRunner.OnFeatureEnd(); + testRunner = null; + } + + [NUnit.Framework.SetUpAttribute()] + public void TestInitialize() + { + } + + [NUnit.Framework.TearDownAttribute()] + public void TestTearDown() + { + testRunner.OnScenarioEnd(); + } + + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public void ScenarioStart() + { + testRunner.OnScenarioStart(); + } + + public void ScenarioCleanup() + { + testRunner.CollectScenarioErrors(); + } + + public virtual void FeatureBackground() + { +#line 5 + #line hidden +#line 6 + testRunner.Given("the demo keyset with event engine enabled", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Successfully receive messages")] + [NUnit.Framework.CategoryAttribute("contract=simpleSubscribe")] + [NUnit.Framework.CategoryAttribute("beta")] + public void SuccessfullyReceiveMessages() + { + string[] tagsOfScenario = new string[] { + "contract=simpleSubscribe", + "beta"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Successfully receive messages", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 9 + this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 5 + this.FeatureBackground(); +#line hidden +#line 10 + testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 11 + testRunner.When("I publish a message", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 12 + testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { + "type", + "name"}); + table1.AddRow(new string[] { + "event", + "SUBSCRIPTION_CHANGED"}); + table1.AddRow(new string[] { + "invocation", + "HANDSHAKE"}); + table1.AddRow(new string[] { + "event", + "HANDSHAKE_SUCCESS"}); + table1.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE"}); + table1.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table1.AddRow(new string[] { + "invocation", + "RECEIVE_EVENTS"}); + table1.AddRow(new string[] { + "event", + "RECEIVE_SUCCESS"}); + table1.AddRow(new string[] { + "invocation", + "CANCEL_RECEIVE_EVENTS"}); + table1.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table1.AddRow(new string[] { + "invocation", + "EMIT_EVENTS"}); +#line 13 + testRunner.And("I observe the following:", ((string)(null)), table1, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Complete handshake failure")] + [NUnit.Framework.CategoryAttribute("contract=subscribeHandshakeFailure")] + [NUnit.Framework.CategoryAttribute("beta")] + public void CompleteHandshakeFailure() + { + string[] tagsOfScenario = new string[] { + "contract=subscribeHandshakeFailure", + "beta"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Complete handshake failure", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 27 + this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 5 + this.FeatureBackground(); +#line hidden +#line 28 + testRunner.Given("a linear reconnection policy with 3 retries", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 29 + testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 30 + testRunner.Then("I receive an error", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { + "type", + "name"}); + table2.AddRow(new string[] { + "event", + "SUBSCRIPTION_CHANGED"}); + table2.AddRow(new string[] { + "invocation", + "HANDSHAKE"}); + table2.AddRow(new string[] { + "event", + "HANDSHAKE_FAILURE"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE"}); + table2.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_FAILURE"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_FAILURE"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_FAILURE"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_GIVEUP"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table2.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); +#line 31 + testRunner.And("I observe the following:", ((string)(null)), table2, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Handshake failure recovery")] + [NUnit.Framework.CategoryAttribute("contract=subscribeHandshakeRecovery")] + [NUnit.Framework.CategoryAttribute("beta")] + public void HandshakeFailureRecovery() + { + string[] tagsOfScenario = new string[] { + "contract=subscribeHandshakeRecovery", + "beta"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Handshake failure recovery", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 52 + this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 5 + this.FeatureBackground(); +#line hidden +#line 53 + testRunner.Given("a linear reconnection policy with 3 retries", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 54 + testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 55 + testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { + "type", + "name"}); + table3.AddRow(new string[] { + "event", + "SUBSCRIPTION_CHANGED"}); + table3.AddRow(new string[] { + "invocation", + "HANDSHAKE"}); + table3.AddRow(new string[] { + "event", + "HANDSHAKE_FAILURE"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE"}); + table3.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table3.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_FAILURE"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table3.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table3.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_SUCCESS"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table3.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table3.AddRow(new string[] { + "invocation", + "RECEIVE_EVENTS"}); + table3.AddRow(new string[] { + "event", + "RECEIVE_SUCCESS"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_RECEIVE_EVENTS"}); + table3.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table3.AddRow(new string[] { + "invocation", + "EMIT_EVENTS"}); +#line 56 + testRunner.And("I observe the following:", ((string)(null)), table3, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Receiving failure recovery")] + [NUnit.Framework.CategoryAttribute("contract=subscribeReceivingRecovery")] + [NUnit.Framework.CategoryAttribute("beta")] + public void ReceivingFailureRecovery() + { + string[] tagsOfScenario = new string[] { + "contract=subscribeReceivingRecovery", + "beta"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Receiving failure recovery", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 76 + this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 5 + this.FeatureBackground(); +#line hidden +#line 77 + testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 78 + testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { + "type", + "name"}); + table4.AddRow(new string[] { + "event", + "SUBSCRIPTION_CHANGED"}); + table4.AddRow(new string[] { + "invocation", + "HANDSHAKE"}); + table4.AddRow(new string[] { + "event", + "HANDSHAKE_SUCCESS"}); + table4.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE"}); + table4.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table4.AddRow(new string[] { + "invocation", + "RECEIVE_EVENTS"}); + table4.AddRow(new string[] { + "event", + "RECEIVE_FAILURE"}); + table4.AddRow(new string[] { + "invocation", + "CANCEL_RECEIVE_EVENTS"}); + table4.AddRow(new string[] { + "invocation", + "RECEIVE_RECONNECT"}); + table4.AddRow(new string[] { + "event", + "RECEIVE_RECONNECT_SUCCESS"}); + table4.AddRow(new string[] { + "invocation", + "CANCEL_RECEIVE_RECONNECT"}); + table4.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table4.AddRow(new string[] { + "invocation", + "EMIT_EVENTS"}); + table4.AddRow(new string[] { + "invocation", + "RECEIVE_EVENTS"}); +#line 79 + testRunner.And("I observe the following:", ((string)(null)), table4, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + } +} +#pragma warning restore +#endregion diff --git a/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs index 299ba3b92..c8bb56301 100644 --- a/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs @@ -27,7 +27,7 @@ public partial class GrantAnAccessTokenFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=access"}; #line 1 "grant-token.feature" @@ -37,10 +37,9 @@ public partial class GrantAnAccessTokenFeature public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Grant an access token", " As a PubNub customer I want to restrict and allow access to\r\n specific PubNub " + - "resources (channels, channel groups, uuids)\r\n by my user base (both people and " + - "devices) which are each\r\n identified by a unique UUID.", ProgrammingLanguage.CSharp, new string[] { - "featureSet=access"}); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Grant an access token", " As a PubNub customer I want to restrict and allow access to\n specific PubNub r" + + "esources (channels, channel groups, uuids)\n by my user base (both people and de" + + "vices) which are each\n identified by a unique UUID.", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } @@ -52,28 +51,28 @@ public virtual void FeatureTearDown() } [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() + public void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] - public virtual void TestTearDown() + public void TestTearDown() { testRunner.OnScenarioEnd(); } - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioInitialize(scenarioInfo); testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public virtual void ScenarioStart() + public void ScenarioStart() { testRunner.OnScenarioStart(); } - public virtual void ScenarioCleanup() + public void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } @@ -91,27 +90,17 @@ public virtual void FeatureBackground() [NUnit.Framework.DescriptionAttribute("Grant an access token with all permissions on all resource types with authorized " + "uuid")] [NUnit.Framework.CategoryAttribute("contract=grantAllPermissions")] - public virtual void GrantAnAccessTokenWithAllPermissionsOnAllResourceTypesWithAuthorizedUuid() + public void GrantAnAccessTokenWithAllPermissionsOnAllResourceTypesWithAuthorizedUuid() { string[] tagsOfScenario = new string[] { "contract=grantAllPermissions"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Grant an access token with all permissions on all resource types with authorized " + - "uuid", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + "uuid", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 12 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -323,26 +312,16 @@ public virtual void GrantAnAccessTokenWithAllPermissionsOnAllResourceTypesWithAu [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Grant an access token without an authorized uuid")] [NUnit.Framework.CategoryAttribute("contract=grantWithoutAuthorizedUUID")] - public virtual void GrantAnAccessTokenWithoutAnAuthorizedUuid() + public void GrantAnAccessTokenWithoutAnAuthorizedUuid() { string[] tagsOfScenario = new string[] { "contract=grantWithoutAuthorizedUUID"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Grant an access token without an authorized uuid", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Grant an access token without an authorized uuid", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 81 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -383,26 +362,16 @@ public virtual void GrantAnAccessTokenWithoutAnAuthorizedUuid() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Grant an access token successfully with an authorized uuid")] [NUnit.Framework.CategoryAttribute("contract=grantWithAuthorizedUUID")] - public virtual void GrantAnAccessTokenSuccessfullyWithAnAuthorizedUuid() + public void GrantAnAccessTokenSuccessfullyWithAnAuthorizedUuid() { string[] tagsOfScenario = new string[] { "contract=grantWithAuthorizedUUID"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Grant an access token successfully with an authorized uuid", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Grant an access token successfully with an authorized uuid", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 92 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -447,27 +416,17 @@ public virtual void GrantAnAccessTokenSuccessfullyWithAnAuthorizedUuid() [NUnit.Framework.DescriptionAttribute("Attempt to grant an access token with all permissions empty or false and expect a" + " server error")] [NUnit.Framework.CategoryAttribute("contract=grantWithoutAnyPermissionsError")] - public virtual void AttemptToGrantAnAccessTokenWithAllPermissionsEmptyOrFalseAndExpectAServerError() + public void AttemptToGrantAnAccessTokenWithAllPermissionsEmptyOrFalseAndExpectAServerError() { string[] tagsOfScenario = new string[] { "contract=grantWithoutAnyPermissionsError"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Attempt to grant an access token with all permissions empty or false and expect a" + - " server error", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + " server error", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 104 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -519,27 +478,17 @@ public virtual void AttemptToGrantAnAccessTokenWithAllPermissionsEmptyOrFalseAnd [NUnit.Framework.DescriptionAttribute("Attempt to grant an access token with a regular expression containing a syntax er" + "ror and expect a server error")] [NUnit.Framework.CategoryAttribute("contract=grantWithRegExpSyntaxError")] - public virtual void AttemptToGrantAnAccessTokenWithARegularExpressionContainingASyntaxErrorAndExpectAServerError() + public void AttemptToGrantAnAccessTokenWithARegularExpressionContainingASyntaxErrorAndExpectAServerError() { string[] tagsOfScenario = new string[] { "contract=grantWithRegExpSyntaxError"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Attempt to grant an access token with a regular expression containing a syntax er" + - "ror and expect a server error", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + "ror and expect a server error", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 118 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -590,27 +539,17 @@ public virtual void AttemptToGrantAnAccessTokenWithARegularExpressionContainingA [NUnit.Framework.DescriptionAttribute("Attempt to grant an access token with a regular expression containing capturing g" + "roups and expect a server error")] [NUnit.Framework.CategoryAttribute("contract=grantWithRegExpNonCapturingError")] - public virtual void AttemptToGrantAnAccessTokenWithARegularExpressionContainingCapturingGroupsAndExpectAServerError() + public void AttemptToGrantAnAccessTokenWithARegularExpressionContainingCapturingGroupsAndExpectAServerError() { string[] tagsOfScenario = new string[] { "contract=grantWithRegExpNonCapturingError"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Attempt to grant an access token with a regular expression containing capturing g" + - "roups and expect a server error", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + "roups and expect a server error", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 132 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -663,28 +602,18 @@ public virtual void AttemptToGrantAnAccessTokenWithARegularExpressionContainingC " (use default max 43200 for the test)")] [NUnit.Framework.CategoryAttribute("contract=grantWithTTLExceedMaxTTL")] [NUnit.Framework.CategoryAttribute("beta")] - public virtual void AttemptToGrantAnAccessTokenWhenTtlProvidedExceedsTheMaxTtlConfiguredUseDefaultMax43200ForTheTest() + public void AttemptToGrantAnAccessTokenWhenTtlProvidedExceedsTheMaxTtlConfiguredUseDefaultMax43200ForTheTest() { string[] tagsOfScenario = new string[] { "contract=grantWithTTLExceedMaxTTL", "beta"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Attempt to grant an access token when ttl provided exceeds the max ttl configured" + - " (use default max 43200 for the test)", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + " (use default max 43200 for the test)", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 146 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } diff --git a/src/UnitTests/AcceptanceTests/Features/members-metadata.feature.cs b/src/UnitTests/AcceptanceTests/Features/members-metadata.feature.cs index 9cfab7e41..c391514fc 100644 --- a/src/UnitTests/AcceptanceTests/Features/members-metadata.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/members-metadata.feature.cs @@ -28,7 +28,7 @@ public partial class ObjectsV2MembersFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=objectsV2", "beta"}; @@ -40,9 +40,7 @@ public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 Members", " As a PubNub customer I want to create, get, remove and update channel members(U" + - "UIDs).", ProgrammingLanguage.CSharp, new string[] { - "featureSet=objectsV2", - "beta"}); + "UIDs).", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } @@ -54,28 +52,28 @@ public virtual void FeatureTearDown() } [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() + public void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] - public virtual void TestTearDown() + public void TestTearDown() { testRunner.OnScenarioEnd(); } - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioInitialize(scenarioInfo); testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public virtual void ScenarioStart() + public void ScenarioStart() { testRunner.OnScenarioStart(); } - public virtual void ScenarioCleanup() + public void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } @@ -92,26 +90,16 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get members for a channel")] [NUnit.Framework.CategoryAttribute("contract=getMembersOfChatChannel")] - public virtual void GetMembersForAChannel() + public void GetMembersForAChannel() { string[] tagsOfScenario = new string[] { "contract=getMembersOfChatChannel"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get members for a channel", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get members for a channel", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 9 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -140,26 +128,16 @@ public virtual void GetMembersForAChannel() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get members for VipChat channel with custom and UUID with custom")] [NUnit.Framework.CategoryAttribute("contract=getMembersOfVipChatChannelWithCustomAndUuidWithCustom")] - public virtual void GetMembersForVipChatChannelWithCustomAndUUIDWithCustom() + public void GetMembersForVipChatChannelWithCustomAndUUIDWithCustom() { string[] tagsOfScenario = new string[] { "contract=getMembersOfVipChatChannelWithCustomAndUuidWithCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get members for VipChat channel with custom and UUID with custom", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get members for VipChat channel with custom and UUID with custom", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 16 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -188,26 +166,16 @@ public virtual void GetMembersForVipChatChannelWithCustomAndUUIDWithCustom() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Set member for a channel")] [NUnit.Framework.CategoryAttribute("contract=setMembersForChatChannel")] - public virtual void SetMemberForAChannel() + public void SetMemberForAChannel() { string[] tagsOfScenario = new string[] { "contract=setMembersForChatChannel"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set member for a channel", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set member for a channel", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 23 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -239,26 +207,16 @@ public virtual void SetMemberForAChannel() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Set member with custom for a channel and UUID with custom")] [NUnit.Framework.CategoryAttribute("contract=setMembersForChatChannelWithCustomAndUuidWithCustom")] - public virtual void SetMemberWithCustomForAChannelAndUUIDWithCustom() + public void SetMemberWithCustomForAChannelAndUUIDWithCustom() { string[] tagsOfScenario = new string[] { "contract=setMembersForChatChannelWithCustomAndUuidWithCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set member with custom for a channel and UUID with custom", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set member with custom for a channel and UUID with custom", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 31 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -290,26 +248,16 @@ public virtual void SetMemberWithCustomForAChannelAndUUIDWithCustom() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Remove member for a channel")] [NUnit.Framework.CategoryAttribute("contract=removeMembersForChatChannel")] - public virtual void RemoveMemberForAChannel() + public void RemoveMemberForAChannel() { string[] tagsOfScenario = new string[] { "contract=removeMembersForChatChannel"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove member for a channel", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove member for a channel", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 39 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -340,28 +288,18 @@ public virtual void RemoveMemberForAChannel() [NUnit.Framework.CategoryAttribute("contract=manageMembersForChatChannel")] [NUnit.Framework.CategoryAttribute("na=ruby")] [NUnit.Framework.CategoryAttribute("na=js")] - public virtual void ManageMembersForAChannel() + public void ManageMembersForAChannel() { string[] tagsOfScenario = new string[] { "contract=manageMembersForChatChannel", "na=ruby", "na=js"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Manage members for a channel", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Manage members for a channel", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 46 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } diff --git a/src/UnitTests/AcceptanceTests/Features/membership-metadata.feature.cs b/src/UnitTests/AcceptanceTests/Features/membership-metadata.feature.cs index 95db4c1c9..3b0874e95 100644 --- a/src/UnitTests/AcceptanceTests/Features/membership-metadata.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/membership-metadata.feature.cs @@ -28,7 +28,7 @@ public partial class ObjectsV2MembershipsFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=objectsV2", "beta"}; @@ -39,9 +39,7 @@ public partial class ObjectsV2MembershipsFeature public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 Memberships", " As a PubNub customer I want to create, update, remove channels.", ProgrammingLanguage.CSharp, new string[] { - "featureSet=objectsV2", - "beta"}); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 Memberships", " As a PubNub customer I want to create, update, remove channels.", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } @@ -53,28 +51,28 @@ public virtual void FeatureTearDown() } [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() + public void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] - public virtual void TestTearDown() + public void TestTearDown() { testRunner.OnScenarioEnd(); } - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioInitialize(scenarioInfo); testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public virtual void ScenarioStart() + public void ScenarioStart() { testRunner.OnScenarioStart(); } - public virtual void ScenarioCleanup() + public void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } @@ -91,26 +89,16 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get memberships for UUID")] [NUnit.Framework.CategoryAttribute("contract=getAliceMemberships")] - public virtual void GetMembershipsForUUID() + public void GetMembershipsForUUID() { string[] tagsOfScenario = new string[] { "contract=getAliceMemberships"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get memberships for UUID", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get memberships for UUID", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 9 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -140,26 +128,16 @@ public virtual void GetMembershipsForUUID() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get memberships for current user")] [NUnit.Framework.CategoryAttribute("contract=getAliceMemberships")] - public virtual void GetMembershipsForCurrentUser() + public void GetMembershipsForCurrentUser() { string[] tagsOfScenario = new string[] { "contract=getAliceMemberships"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get memberships for current user", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get memberships for current user", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 16 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -189,26 +167,16 @@ public virtual void GetMembershipsForCurrentUser() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get memberships for UUID with custom and channel custom")] [NUnit.Framework.CategoryAttribute("contract=getBobMembershipWithCustomAndChannelCustom")] - public virtual void GetMembershipsForUUIDWithCustomAndChannelCustom() + public void GetMembershipsForUUIDWithCustomAndChannelCustom() { string[] tagsOfScenario = new string[] { "contract=getBobMembershipWithCustomAndChannelCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get memberships for UUID with custom and channel custom", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get memberships for UUID with custom and channel custom", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 23 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -238,26 +206,16 @@ public virtual void GetMembershipsForUUIDWithCustomAndChannelCustom() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Set membership")] [NUnit.Framework.CategoryAttribute("contract=setAliceMembership")] - public virtual void SetMembership() + public void SetMembership() { string[] tagsOfScenario = new string[] { "contract=setAliceMembership"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set membership", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set membership", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 30 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -289,26 +247,16 @@ public virtual void SetMembership() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Set membership for current user")] [NUnit.Framework.CategoryAttribute("contract=setAliceMembership")] - public virtual void SetMembershipForCurrentUser() + public void SetMembershipForCurrentUser() { string[] tagsOfScenario = new string[] { "contract=setAliceMembership"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set membership for current user", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set membership for current user", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 38 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -340,26 +288,16 @@ public virtual void SetMembershipForCurrentUser() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Remove membership")] [NUnit.Framework.CategoryAttribute("contract=removeAliceMembership")] - public virtual void RemoveMembership() + public void RemoveMembership() { string[] tagsOfScenario = new string[] { "contract=removeAliceMembership"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove membership", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove membership", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 46 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -388,26 +326,16 @@ public virtual void RemoveMembership() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Remove membership for current user")] [NUnit.Framework.CategoryAttribute("contract=removeAliceMembership")] - public virtual void RemoveMembershipForCurrentUser() + public void RemoveMembershipForCurrentUser() { string[] tagsOfScenario = new string[] { "contract=removeAliceMembership"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove membership for current user", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove membership for current user", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 53 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -436,26 +364,16 @@ public virtual void RemoveMembershipForCurrentUser() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Manage memberships for a UUID")] [NUnit.Framework.CategoryAttribute("contract=manageAliceMemberships")] - public virtual void ManageMembershipsForAUUID() + public void ManageMembershipsForAUUID() { string[] tagsOfScenario = new string[] { "contract=manageAliceMemberships"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Manage memberships for a UUID", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Manage memberships for a UUID", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 60 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } diff --git a/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs index 9f5649309..f98f833b4 100644 --- a/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs @@ -28,7 +28,7 @@ public partial class RevokeAnAccessTokenFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=access", "beta"}; @@ -39,10 +39,8 @@ public partial class RevokeAnAccessTokenFeature public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Revoke an access token", " As a PubNub customer I want to withdraw existing permission for\r\n specific Pub" + - "Nub resources by revoking corresponding tokens.", ProgrammingLanguage.CSharp, new string[] { - "featureSet=access", - "beta"}); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Revoke an access token", " As a PubNub customer I want to withdraw existing permission for\n specific PubN" + + "ub resources by revoking corresponding tokens.", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } @@ -54,28 +52,28 @@ public virtual void FeatureTearDown() } [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() + public void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] - public virtual void TestTearDown() + public void TestTearDown() { testRunner.OnScenarioEnd(); } - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioInitialize(scenarioInfo); testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public virtual void ScenarioStart() + public void ScenarioStart() { testRunner.OnScenarioStart(); } - public virtual void ScenarioCleanup() + public void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } @@ -92,26 +90,16 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Revoke existing valid token")] [NUnit.Framework.CategoryAttribute("contract=revokeValidToken")] - public virtual void RevokeExistingValidToken() + public void RevokeExistingValidToken() { string[] tagsOfScenario = new string[] { "contract=revokeValidToken"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke existing valid token", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke existing valid token", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 10 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -137,26 +125,16 @@ public virtual void RevokeExistingValidToken() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Revoke invalid token")] [NUnit.Framework.CategoryAttribute("contract=revokeInvalidToken")] - public virtual void RevokeInvalidToken() + public void RevokeInvalidToken() { string[] tagsOfScenario = new string[] { "contract=revokeInvalidToken"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke invalid token", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke invalid token", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 16 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -203,26 +181,16 @@ public virtual void RevokeInvalidToken() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Revoke a token while it is disabled on a server")] [NUnit.Framework.CategoryAttribute("contract=revokeFeatureDisabled")] - public virtual void RevokeATokenWhileItIsDisabledOnAServer() + public void RevokeATokenWhileItIsDisabledOnAServer() { string[] tagsOfScenario = new string[] { "contract=revokeFeatureDisabled"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke a token while it is disabled on a server", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke a token while it is disabled on a server", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 29 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -269,26 +237,16 @@ public virtual void RevokeATokenWhileItIsDisabledOnAServer() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Revoke a token with characters that require url encoding")] [NUnit.Framework.CategoryAttribute("contract=revokeEncodePathParameter")] - public virtual void RevokeATokenWithCharactersThatRequireUrlEncoding() + public void RevokeATokenWithCharactersThatRequireUrlEncoding() { string[] tagsOfScenario = new string[] { "contract=revokeEncodePathParameter"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke a token with characters that require url encoding", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Revoke a token with characters that require url encoding", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 42 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } diff --git a/src/UnitTests/AcceptanceTests/Features/uuid-metadata.feature.cs b/src/UnitTests/AcceptanceTests/Features/uuid-metadata.feature.cs index 25e3dae05..2da1dd2fb 100644 --- a/src/UnitTests/AcceptanceTests/Features/uuid-metadata.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/uuid-metadata.feature.cs @@ -28,7 +28,7 @@ public partial class ObjectsV2UUIDMetadataFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=objectsV2", "beta"}; @@ -39,9 +39,7 @@ public partial class ObjectsV2UUIDMetadataFeature public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 UUID metadata", " As a PubNub customer I want to create, update, remove uuids.", ProgrammingLanguage.CSharp, new string[] { - "featureSet=objectsV2", - "beta"}); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Objects V2 UUID metadata", " As a PubNub customer I want to create, update, remove uuids.", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } @@ -53,28 +51,28 @@ public virtual void FeatureTearDown() } [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() + public void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] - public virtual void TestTearDown() + public void TestTearDown() { testRunner.OnScenarioEnd(); } - public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioInitialize(scenarioInfo); testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public virtual void ScenarioStart() + public void ScenarioStart() { testRunner.OnScenarioStart(); } - public virtual void ScenarioCleanup() + public void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } @@ -91,26 +89,16 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get a UUID metadata for id")] [NUnit.Framework.CategoryAttribute("contract=getUUIDMetadataOfAlice")] - public virtual void GetAUUIDMetadataForId() + public void GetAUUIDMetadataForId() { string[] tagsOfScenario = new string[] { "contract=getUUIDMetadataOfAlice"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a UUID metadata for id", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a UUID metadata for id", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 9 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -139,26 +127,16 @@ public virtual void GetAUUIDMetadataForId() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get a UUID with custom metadata, id stored in config")] [NUnit.Framework.CategoryAttribute("contract=getUUIDMetadataOfBobWithCustom")] - public virtual void GetAUUIDWithCustomMetadataIdStoredInConfig() + public void GetAUUIDWithCustomMetadataIdStoredInConfig() { string[] tagsOfScenario = new string[] { "contract=getUUIDMetadataOfBobWithCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a UUID with custom metadata, id stored in config", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get a UUID with custom metadata, id stored in config", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 16 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -187,26 +165,16 @@ public virtual void GetAUUIDWithCustomMetadataIdStoredInConfig() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Set a UUID metadata")] [NUnit.Framework.CategoryAttribute("contract=setUUIDMetadataForAlice")] - public virtual void SetAUUIDMetadata() + public void SetAUUIDMetadata() { string[] tagsOfScenario = new string[] { "contract=setUUIDMetadataForAlice"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set a UUID metadata", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Set a UUID metadata", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 23 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -235,26 +203,16 @@ public virtual void SetAUUIDMetadata() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Remove a UUID metadata for id")] [NUnit.Framework.CategoryAttribute("contract=removeUUIDMetadataOfAlice")] - public virtual void RemoveAUUIDMetadataForId() + public void RemoveAUUIDMetadataForId() { string[] tagsOfScenario = new string[] { "contract=removeUUIDMetadataOfAlice"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove a UUID metadata for id", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove a UUID metadata for id", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 30 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -280,26 +238,16 @@ public virtual void RemoveAUUIDMetadataForId() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Remove a UUID metadata, id stored in config")] [NUnit.Framework.CategoryAttribute("contract=removeUUIDMetadataOfAlice")] - public virtual void RemoveAUUIDMetadataIdStoredInConfig() + public void RemoveAUUIDMetadataIdStoredInConfig() { string[] tagsOfScenario = new string[] { "contract=removeUUIDMetadataOfAlice"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove a UUID metadata, id stored in config", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Remove a UUID metadata, id stored in config", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 36 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -325,26 +273,16 @@ public virtual void RemoveAUUIDMetadataIdStoredInConfig() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get all UUID metadata")] [NUnit.Framework.CategoryAttribute("contract=getAllUUIDMetadata")] - public virtual void GetAllUUIDMetadata() + public void GetAllUUIDMetadata() { string[] tagsOfScenario = new string[] { "contract=getAllUUIDMetadata"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all UUID metadata", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all UUID metadata", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 42 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } @@ -370,26 +308,16 @@ public virtual void GetAllUUIDMetadata() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Get all UUID metadata with custom")] [NUnit.Framework.CategoryAttribute("contract=getAllUUIDMetadataWithCustom")] - public virtual void GetAllUUIDMetadataWithCustom() + public void GetAllUUIDMetadataWithCustom() { string[] tagsOfScenario = new string[] { "contract=getAllUUIDMetadataWithCustom"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all UUID metadata with custom", null, tagsOfScenario, argumentsOfScenario, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get all UUID metadata with custom", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 48 this.ScenarioInitialize(scenarioInfo); #line hidden - bool isScenarioIgnored = default(bool); - bool isFeatureIgnored = default(bool); - if ((tagsOfScenario != null)) - { - isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((this._featureTags != null)) - { - isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any(); - } - if ((isScenarioIgnored || isFeatureIgnored)) + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) { testRunner.SkipScenario(); } diff --git a/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs new file mode 100644 index 000000000..9b2edae6b --- /dev/null +++ b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs @@ -0,0 +1,382 @@ +using System; +using TechTalk.SpecFlow; +using PubnubApi; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using System.Net; +using System.Globalization; +using System.IO; +using System.Text.Json; +using System.Threading.Channels; +using System.Threading; +using PubnubApi.PubnubEventEngine; +using TechTalk.SpecFlow.Assist; +using System.Net.Http; + +namespace AcceptanceTests.Steps +{ + [Binding] + public class EventEngineSteps + { + public static bool enableIntenalPubnubLogging = false; + public static string currentFeature = string.Empty; + public static string currentContract = string.Empty; + public static bool betaVersion = false; + private string acceptance_test_origin = "localhost:8090"; + private bool bypassMockServer = false; + private readonly ScenarioContext _scenarioContext; + private Pubnub pn; + private PNConfiguration config = null; + private string channel = "my_channel"; + private string channelGroup = "my_channelgroup"; + private string publishMsg = "hello_world"; + //private UuidMetadataPersona uuidMetadataPersona = null; + //private PNGetUuidMetadataResult getUuidMetadataResult = null; + //private PNSetUuidMetadataResult setUuidMetadataResult = null; + //private PNGetAllUuidMetadataResult getAllUuidMetadataResult = null; + PNPublishResult publishResult = null; + SubscribeCallback subscribeCallback = null; + private PNMessageResult messageResult = null; + ManualResetEvent messageReceivedEvent = new ManualResetEvent(false); + ManualResetEvent statusReceivedEvent = new ManualResetEvent(false); + PNStatus pnStatus = null; + PubnubError pnError = null; + IPubnubUnitTest unitTest; + + public class PubnubUnitTest : IPubnubUnitTest + { + long IPubnubUnitTest.Timetoken + { + get; + set; + } + + string IPubnubUnitTest.RequestId + { + get; + set; + } + + byte[] IPubnubUnitTest.IV + { + get; + set; + } + + bool IPubnubUnitTest.InternetAvailable + { + get; + set; + } + + string IPubnubUnitTest.SdkVersion + { + get; + set; + } + + bool IPubnubUnitTest.IncludePnsdk + { + get; + set; + } + + bool IPubnubUnitTest.IncludeUuid + { + get; + set; + } + List> IPubnubUnitTest.EventTypeList + { + get; + set; + } + int IPubnubUnitTest.Attempts + { + get; + set; + } + } + public class PubnubError + { + public ErrorMsg error; + public string service; + public int status; + } + public class MessageDetail + { + public string message; + public string location; + public string locationType; + } + public class ErrorMsg + { + public string message; + public string source; + public List details; + } + + public class InternalPubnubLog : IPubnubLog + { + void IPubnubLog.WriteToLog(string logText) + { + System.Diagnostics.Debug.WriteLine(logText); + string dirPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + string logFilePath = System.IO.Path.Combine(dirPath, "pubnubmessaging.log"); + System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.TextWriterTraceListener(logFilePath)); + + } + } + public class SubscribeResponseRow + { + public string type { get; set; } + public string name { get; set; } + } + public EventEngineSteps(ScenarioContext scenarioContext) + { + _scenarioContext = scenarioContext; + } + + [BeforeFeature] + public static void BeforeFeature(FeatureContext featureContext) + { + betaVersion = false; + if (featureContext.FeatureInfo != null && featureContext.FeatureInfo.Tags.Length > 0) + { + List tagList = featureContext.FeatureInfo.Tags.AsEnumerable().ToList(); + foreach (string tag in tagList) + { + if (tag.IndexOf("featureSet=") == 0) + { + currentFeature = tag.Replace("featureSet=", ""); + } + + if (tag.IndexOf("beta") == 0) + { + betaVersion = true; + } + } + } + + System.Diagnostics.Debug.WriteLine("Starting " + featureContext.FeatureInfo.Title); + } + + [AfterFeature] + public static void AfterFeature(FeatureContext featureContext) + { + System.Diagnostics.Debug.WriteLine("Finished " + featureContext.FeatureInfo.Title); + } + + [BeforeScenario()] + public void BeforeScenario() + { + currentContract = ""; + if (_scenarioContext.ScenarioInfo != null && _scenarioContext.ScenarioInfo.Tags.Length > 0) + { + List tagList = _scenarioContext.ScenarioInfo.Tags.AsEnumerable().ToList(); + foreach (string tag in tagList) + { + if (tag.IndexOf("contract=") == 0) + { + currentContract = tag.Replace("contract=", ""); + break; + } + } + if (!string.IsNullOrEmpty(currentContract) && !bypassMockServer) + { + string mockInitContract = string.Format("http://{0}/init?__contract__script__={1}", acceptance_test_origin, currentContract); + System.Diagnostics.Debug.WriteLine(mockInitContract); + HttpClient httpclient = new HttpClient(); + string mockInitResponse = httpclient.GetStringAsync(new Uri(mockInitContract)).Result; + System.Diagnostics.Debug.WriteLine(mockInitResponse); + } + } + + } + + [AfterScenario()] + public void AfterScenario() + { + if (!bypassMockServer) + { + string mockExpectContract = string.Format("http://{0}/expect", acceptance_test_origin); + System.Diagnostics.Debug.WriteLine(mockExpectContract); + WebClient webClient = new WebClient(); + string mockExpectResponse = webClient.DownloadString(mockExpectContract); + System.Diagnostics.Debug.WriteLine(mockExpectResponse); + } + } + [Given(@"the demo keyset with event engine enabled")] + public void GivenTheDemoKeysetWithEventEngineEnabled() + { + unitTest = new PubnubUnitTest(); + unitTest.Timetoken = 16820876821905844; //Hardcoded timetoken + unitTest.RequestId = "myRequestId"; + unitTest.InternetAvailable = true; + unitTest.SdkVersion = "Csharp"; + unitTest.IncludePnsdk = true; + unitTest.IncludeUuid = true; + + config = new PNConfiguration(new UserId("pn-csharp-acceptance-test-uuid")); + config.Origin = acceptance_test_origin; + config.Secure = false; + config.PublishKey = System.Environment.GetEnvironmentVariable("PN_PUB_KEY"); + config.SubscribeKey = System.Environment.GetEnvironmentVariable("PN_SUB_KEY"); + config.SecretKey = System.Environment.GetEnvironmentVariable("PN_SEC_KEY"); + if (enableIntenalPubnubLogging) + { + config.LogVerbosity = PNLogVerbosity.BODY; + config.PubnubLog = new InternalPubnubLog(); + } + else + { + config.LogVerbosity = PNLogVerbosity.NONE; + } + config.EnableEventEngine = true; + + + messageReceivedEvent = new ManualResetEvent(false); + statusReceivedEvent = new ManualResetEvent(false); + + subscribeCallback = new SubscribeCallbackExt( + delegate (Pubnub pnObj, PNMessageResult pubMsg) + { + Console.WriteLine($"Message received in listener. {pn.JsonPluggableLibrary.SerializeToJsonString(pubMsg)}"); + messageResult = pubMsg; + messageReceivedEvent.Set(); + }, + delegate (Pubnub pnObj, PNPresenceEventResult presenceEvnt) + { + Console.WriteLine(pn.JsonPluggableLibrary.SerializeToJsonString(presenceEvnt)); + }, + delegate (Pubnub pnObj, PNSignalResult signalMsg) + { + Console.WriteLine(pn.JsonPluggableLibrary.SerializeToJsonString(signalMsg)); + }, + delegate (Pubnub pnObj, PNObjectEventResult objectEventObj) + { + Console.WriteLine(pn.JsonPluggableLibrary.SerializeToJsonString(objectEventObj)); + }, + delegate (Pubnub pnObj, PNMessageActionEventResult msgActionEvent) + { + System.Diagnostics.Debug.WriteLine(pn.JsonPluggableLibrary.SerializeToJsonString(msgActionEvent)); + }, + delegate (Pubnub pnObj, PNFileEventResult fileEvent) + { + System.Diagnostics.Debug.WriteLine(pn.JsonPluggableLibrary.SerializeToJsonString(fileEvent)); + }, + delegate (Pubnub pnObj, PNStatus status) + { + pnStatus = status; + Console.WriteLine("{0} {1} {2}", pnStatus.Operation, pnStatus.Category, pnStatus.StatusCode); + if (currentContract == "subscribeHandshakeFailure" && pn.PubnubUnitTest.Attempts == 3) + { + statusReceivedEvent.Set(); + } + if (pnStatus.Category == PNStatusCategory.PNConnectedCategory) + { + statusReceivedEvent.Set(); + } + } + ); + + } + + [When(@"I subscribe")] + public void WhenISubscribe() + { + pn = new Pubnub(config); + pn.PubnubUnitTest = unitTest; + pn.PubnubUnitTest.EventTypeList?.Clear(); + + messageReceivedEvent = new ManualResetEvent(false); + statusReceivedEvent = new ManualResetEvent(false); + + pn.AddListener(subscribeCallback); + pn.Subscribe() + .Channels(channel.Split(',')) + .ChannelGroups(channelGroup.Split(',')) + .Execute(); + statusReceivedEvent.WaitOne (60*1000); + if (pnStatus != null && pnStatus.Category == PNStatusCategory.PNConnectedCategory) + { + //All good. + } + else + { + if (currentContract == "simpleSubscribe") + { + Assert.Fail("WhenISubscribe failed."); + } + } + } + + [When(@"I publish a message")] + public async Task WhenIPublishAMessage() + { + PNResult publishResponse = await pn.Publish() + .Channel(channel) + .Message(publishMsg) + .ExecuteAsync(); + publishResult = publishResponse.Result; + pnStatus = publishResponse.Status; + } + + [Then(@"I receive the message in my subscribe response")] + public async Task ThenIReceiveTheMessageInMySubscribeResponse() + { + await Task.Delay(1000); + messageReceivedEvent.WaitOne(); + Assert.True(messageResult != null); + } + + [Then(@"I observe the following:")] + public void ThenIObserveTheFollowing(Table table) + { + if (pn.PubnubUnitTest == null) + { + Assert.Fail(); + } + System.Diagnostics.Debug.WriteLine($"COUNT = {pn.PubnubUnitTest.EventTypeList.Count} "); + for (int i = 0; i < pn.PubnubUnitTest.EventTypeList.Count(); i++) + { + System.Diagnostics.Debug.WriteLine($"{pn.PubnubUnitTest.EventTypeList[i].Key} - {pn.PubnubUnitTest.EventTypeList[i].Value} "); + } + IEnumerable expectedRowSet = table.CreateSet(); + Assert.True(pn.PubnubUnitTest.EventTypeList.Count() >= expectedRowSet?.Count()); + bool match = false; + for (int rowIndex = 0; rowIndex < expectedRowSet.Count(); rowIndex++) + { + SubscribeResponseRow row = expectedRowSet.ElementAt(rowIndex); + System.Diagnostics.Debug.WriteLine($"{row.type} - {row.name} "); + if (row.type == pn.PubnubUnitTest.EventTypeList[rowIndex].Key + && row.name == pn.PubnubUnitTest.EventTypeList[rowIndex].Value) + { + match = true; + } + else + { + match = false; + break; + } + } + Assert.True(match == true); + } + + [Given(@"a linear reconnection policy with (.*) retries")] + public void GivenALinearReconnectionPolicyWithRetries(int retryCount) + { + config.ReconnectionPolicy = PNReconnectionPolicy.LINEAR; + config.ConnectionMaxRetries = retryCount; + } + + [Then(@"I receive an error")] + public void ThenIReceiveAnError() + { + Assert.True(pnStatus != null && pnStatus.Error); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs new file mode 100644 index 000000000..d60b61302 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs @@ -0,0 +1,81 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.States; + +namespace PubnubApi.Tests.EventEngine +{ + internal class HandshakeFailedStateTransition + { + private static object[] handshakeFailedEventCases = { + new object[] { + new HandshakeFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new HandshakingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" } } + }, + new object[] + { + new HandshakeFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + }, + new object[] + { + new HandshakeFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new ReconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + } + }; + + [TestCaseSource(nameof(handshakeFailedEventCases))] + public void HandshakeFailedState_OnEvent_TransitionsToHandshakingState( + HandshakeFailedState handshakeFailedState, IEvent @event, HandshakingState expectedState) + { + //Act + var result = handshakeFailedState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + Assert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + Assert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + if (@event is ReconnectEvent reconnectEvent) + { + Assert.AreEqual(reconnectEvent.Cursor, ((HandshakingState)result.State).Cursor); + } + } + + [TestCase] + public void HandshakeFailedState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + // Arrange + var handshakeFailedState = new HandshakeFailedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + + var @event = new UnsubscribeAllEvent() { }; + + //Act + var result = handshakeFailedState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs new file mode 100644 index 000000000..9d214e61a --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs @@ -0,0 +1,176 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class HandshakeReconnectingStateTransition + { + private static object[] handshakeReconnectingEventCases = { + new object[] { + new HandshakeReconnectingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new HandshakingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" } } + }, + new object[] + { + new HandshakeReconnectingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + } + }; + + [TestCaseSource(nameof(handshakeReconnectingEventCases))] + public void HandshakeReconnectingState_OnEvent_TransitionToHandshakingState( + HandshakeReconnectingState handshakeReconnectingState, IEvent @event, HandshakingState expectedState) + { + //Act + var result = handshakeReconnectingState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + Assert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + Assert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + } + + private HandshakeReconnectingState CreateHandshakeReconnectingState() + { + return new HandshakeReconnectingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } , + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + } + + [Test] + public void HandshakeReconnectingState_OnHandshakeReconnectFailureEvent_TransitionToHandshakeReconnectingState() + { + //Arrange + var currentState = CreateHandshakeReconnectingState(); + var eventToTriggerTransition = new HandshakeReconnectFailureEvent(); + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(currentState.Channels, ((HandshakeReconnectingState)result.State).Channels); + CollectionAssert.AreEqual(currentState.ChannelGroups, ((HandshakeReconnectingState)result.State).ChannelGroups); + + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNUnknownCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void HandshakeReconnectingState_OnDisconnectEvent_TransitionToHandshakeStoppedState() + { + //Arrange + var currentState = CreateHandshakeReconnectingState(); + var eventToTriggerTransition = new DisconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + var expectedState = new HandshakeStoppedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakeStoppedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakeStoppedState)result.State).ChannelGroups); + } + + [Test] + public void HandshakeReconnectingState_OnHandshakeReconnectGiveupEvent_TransitionToHandshakeFailedState() + { + //Arrange + var currentState = CreateHandshakeReconnectingState(); + var eventToTriggerTransition = new HandshakeReconnectGiveUpEvent() { }; + var expectedState = new HandshakeFailedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + + //Act + var result = currentState.Transition(@eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakeFailedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakeFailedState)result.State).ChannelGroups); + + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNUnknownCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void HandshakeReconnectingState_OnHandshakeReconnectSuccessEvent_TransitionToReceivingState() + { + //Arrange + var currentState = CreateHandshakeReconnectingState(); + var eventToTriggerTransition = new HandshakeReconnectSuccessEvent() + { + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + Status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, currentState.Channels, currentState.ChannelGroups) + }; + var expectedState = new ReceivingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = currentState.Transition(@eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + + CollectionAssert.AreEqual(expectedState.Channels, ((ReceivingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceivingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceivingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceivingState)result.State).Cursor.Timetoken); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((ReceivingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((ReceivingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNConnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void HandshakeReconnectingState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + // Arrange + var currentState = CreateHandshakeReconnectingState(); + var eventToTriggerTransition = new UnsubscribeAllEvent(); + + // Act + var result = currentState.Transition(eventToTriggerTransition); + + // Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeStoppedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeStoppedStateTransition.cs new file mode 100644 index 000000000..abf813e93 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeStoppedStateTransition.cs @@ -0,0 +1,81 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class HandshakeStoppedStateTransition + { + private static object[] handshakeStoppedEventCases = { + new object[] { + new HandshakeStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new HandshakingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" } } + }, + new object[] + { + new HandshakeStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + }, + new object[] + { + new HandshakeStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } }, + new ReconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + } + }; + + [TestCaseSource(nameof(handshakeStoppedEventCases))] + public void HandshakeStoppedState_OnEvent_TransitionToHandshakingState( + HandshakeStoppedState handshakeStoppedState, IEvent @event, HandshakingState expectedState) + { + //Act + var result = handshakeStoppedState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + if (@event is ReconnectEvent reconnectEvent) + { + Assert.AreEqual(reconnectEvent.Cursor, ((HandshakingState)result.State).Cursor); + } + } + + [Test] + public void HandshakeStoppedState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + //Arrange + var handshakeStoppedState = new HandshakeStoppedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + var @event = new UnsubscribeAllEvent() { }; + + //Act + var result = handshakeStoppedState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs new file mode 100644 index 000000000..55e75a9af --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs @@ -0,0 +1,169 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class HandshakingStateTransition + { + private static object[] handshakingEventCases = { + new object[] { + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new HandshakingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) } + }, + new object[] + { + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new HandshakingState(){ Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) } + } + }; + + [TestCaseSource(nameof(handshakingEventCases))] + public void HandshakingState_OnEvent_TransitionToHandshakingState( + HandshakingState handshakingState, IEvent @event, HandshakingState expectedState) + { + //Act + var result = handshakingState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + Assert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + Assert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((HandshakingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((HandshakingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + if (@event is SubscriptionRestoredEvent) + { + Assert.AreEqual(expectedState.Cursor.Region, ((HandshakingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((HandshakingState)result.State).Cursor.Timetoken); + } + } + + private HandshakingState CreateHandshakingState() + { + return new HandshakingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + } + + [Test] + public void HandshakingState_OnHandshakeFailureEvent_TransitionToHandshakeReconnectingState() + { + //Arrange + var currentState = CreateHandshakingState(); + var eventToTriggerTransition = new HandshakeFailureEvent() { }; + var expectedState = new HandshakeReconnectingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakeReconnectingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakeReconnectingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((HandshakeReconnectingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((HandshakeReconnectingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNUnknownCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void HandshakingState_OnDisconnectEvent_TransitionToHandshakeStoppedState() + { + //Arrange + var handshakingState = CreateHandshakingState(); + var eventToTriggerTransition = new DisconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + var expectedState = new HandshakeStoppedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = handshakingState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakeStoppedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakeStoppedState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((HandshakeStoppedState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((HandshakeStoppedState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNDisconnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void HandshakingState_OnHandshakeSuccessEvent_TransitionToReceivingState() + { + //Arrange + var handshakingState = CreateHandshakingState(); + var eventToTriggerTransition = new HandshakeSuccessEvent() + { + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + Status = new PNStatus(null,PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, handshakingState.Channels, handshakingState.ChannelGroups) + }; + var expectedState = new ReceivingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = handshakingState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceivingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceivingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((ReceivingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((ReceivingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNConnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void HandshakingState_OnUnsubscribeEvent_TransitionToUnsubscribedState() + { + //Arrange + var currentState = CreateHandshakingState(); + var eventToTriggerTransition = new UnsubscribeAllEvent(); + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs new file mode 100644 index 000000000..e4f85e7a8 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs @@ -0,0 +1,78 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class ReceiveFailedStateTransition + { + private static object[] receiveFailedEventCases = { + new object[] { + new ReceiveFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new HandshakingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" } } + }, + new object[] + { + new ReceiveFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + }, + new object[] + { + new ReceiveFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }, + new ReconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new HandshakingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" } } + } + }; + + [TestCaseSource(nameof(receiveFailedEventCases))] + public void ReceiveFailedState_OnEvent_TransitionToHandshakingState( + ReceiveFailedState receiveFailedState, IEvent @event, HandshakingState expectedState) + { + //Act + var result = receiveFailedState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + Assert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + Assert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + if (@event is ReconnectEvent reconnectEvent) + { + Assert.AreEqual(reconnectEvent.Cursor, ((HandshakingState)result.State).Cursor); + } + } + + [Test] + public void ReceiveFailedState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + //Arrange + var receiveFailedState = new ReceiveFailedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }; + var @event = new UnsubscribeAllEvent() { }; + + //Act + var result = receiveFailedState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs new file mode 100644 index 000000000..3b265a847 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs @@ -0,0 +1,178 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class ReceiveReconnectingStateTransition + { + private static object[] receiveReconnectingEventCases = { + new object[] { + new ReceiveReconnectingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new ReceivingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } } + }, + new object[] + { + new ReceiveReconnectingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new ReceivingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } } + }, + new object[] + { + new ReceiveReconnectingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new ReceiveReconnectSuccessEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + Status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNReconnectedCategory) + }, + new ReceivingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } } + } + + }; + + [TestCaseSource(nameof(receiveReconnectingEventCases))] + public void ReceiveReconnectingState_OnEvent_TransitionToReceivingState( + ReceiveReconnectingState receiveReconnectingState, IEvent @event, ReceivingState expectedState) + { + //Act + var result = receiveReconnectingState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceivingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceivingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceivingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceivingState)result.State).Cursor.Timetoken); + if (@event is ReceiveReconnectSuccessEvent) + { + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNReconnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + } + + private ReceiveReconnectingState CreateReceiveReconnectingState() + { + return new ReceiveReconnectingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }; + } + + [Test] + public void ReceiveReconnectingState_OnReceiveReconnectFailureEvent_TransitionToReceiveReconnectingState() + { + //Arrange + var currentState = CreateReceiveReconnectingState(); + var eventToTriggerTransition = new ReceiveReconnectFailureEvent() { }; + var expectedState = new ReceiveReconnectingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceiveReconnectingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceiveReconnectingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceiveReconnectingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceiveReconnectingState)result.State).Cursor.Timetoken); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNUnknownCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void ReceiveReconnectingState_OnDisconnectEvent_TransitionToReceiveStoppedState() + { + //Arrange + var currentState = CreateReceiveReconnectingState(); + var eventToTriggerTransition = new DisconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + var expectedState = new ReceiveStoppedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceiveStoppedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceiveStoppedState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceiveStoppedState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceiveStoppedState)result.State).Cursor.Timetoken); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNDisconnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void ReceiveReconnectingState_OnReceiveReconnectGiveupEvent_TransitionToReceiveFailedState() + { + //Arrange + var currentState = CreateReceiveReconnectingState(); + var eventToTriggerTransition = new ReceiveReconnectGiveUpEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + var expectedState = new ReceiveFailedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceiveFailedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceiveFailedState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceiveFailedState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceiveFailedState)result.State).Cursor.Timetoken); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNUnknownCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void ReceiveReconnectingState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + //Arrange + var currentState = CreateReceiveReconnectingState(); + var eventToTriggerTransition = new UnsubscribeAllEvent(); + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs new file mode 100644 index 000000000..f560f8a14 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs @@ -0,0 +1,95 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class ReceiveStoppedStateTransition + { + private static object[] receiveStoppedEventCases = { + new object[] { + new ReceiveStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new ReceiveStoppedState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } } + }, + new object[] + { + new ReceiveStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new ReceiveStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } } + }, + }; + + [TestCase] + public void ReceiveStoppedState_OnReconnectEvent_TransitionToHandshakingState() + { + //Arrange + + var currentState = new ReceiveStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }; + var eventToTriggerTransition = new ReconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + var expectedState = new HandshakingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((HandshakingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((HandshakingState)result.State).Cursor.Timetoken); + } + + [TestCaseSource(nameof(receiveStoppedEventCases))] + public void ReceiveStoppedState_OnEvent_TransitionToReceiveStoppedState( + ReceiveStoppedState receiveStoppedState, IEvent @event, ReceiveStoppedState expectedState) + { + //Act + var result = receiveStoppedState.Transition(@event); + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceiveStoppedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceiveStoppedState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceiveStoppedState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceiveStoppedState)result.State).Cursor.Timetoken); + } + + [Test] + public void ReceiveStoppedState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + //Arrange + var currentState = new ReceiveStoppedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } }; + var eventToTriggerTransition = new UnsubscribeAllEvent(); + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs new file mode 100644 index 000000000..50e3fafd5 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs @@ -0,0 +1,155 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class ReceivingStateTransition + { + private static object[] receivingEventCases = { + new object[] { + new ReceivingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2", "ch3" }, + ChannelGroups = new string[] { "cg1", "cg2", "cg3" } + }, + new ReceivingState(){ Channels = new string[] { "ch1", "ch2", "ch3" }, ChannelGroups = new string[] { "cg1", "cg2", "cg3" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) } + }, + new object[] + { + new ReceivingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }, + new ReceivingState(){ Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) } + }, + new object[] + { + new ReceivingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }, + new ReceiveSuccessEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + Status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory), + Messages = new ReceivingResponse() { Messages = new Message[]{ }, Timetoken = new Timetoken(){ Region = 1, Timestamp = 1234567890 } } + }, + new ReceivingState(){ Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) } + } + + }; + + [TestCaseSource(nameof(receivingEventCases))] + public void ReceivingState_OnEvent_TransitionToReceivingState( + ReceivingState receivingState, IEvent @event, ReceivingState expectedState) + { + //Act + var result = receivingState.Transition(@event); + + //Assert + Assert.IsInstanceOf(result.State); + Assert.AreEqual(expectedState.Channels, ((ReceivingState)result.State).Channels); + Assert.AreEqual(expectedState.ChannelGroups, ((ReceivingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((ReceivingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((ReceivingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + if (@event is SubscriptionRestoredEvent || @event is ReceiveSuccessEvent) + { + Assert.AreEqual(expectedState.Cursor.Region, ((ReceivingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceivingState)result.State).Cursor.Timetoken); + } + if (@event is ReceiveSuccessEvent) + { + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.IsInstanceOf(result.Invocations.ElementAt(1)); + Assert.AreEqual(PNStatusCategory.PNConnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(1)).StatusCategory); + } + } + + private ReceivingState CreateReceivingState() + { + return new ReceivingState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }; + } + + [Test] + public void ReceivingState_OnDisconnectEvent_TransitionToReceiveStoppedState() + { + //Arrange + var currentState = CreateReceivingState(); + var eventToTriggerTransition = new DisconnectEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + var expectedState = new ReceiveStoppedState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceiveStoppedState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceiveStoppedState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceiveStoppedState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceiveStoppedState)result.State).Cursor.Timetoken); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((ReceiveStoppedState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((ReceiveStoppedState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + Assert.IsInstanceOf(result.Invocations.ElementAt(0)); + Assert.AreEqual(PNStatusCategory.PNDisconnectedCategory, ((EmitStatusInvocation)result.Invocations.ElementAt(0)).StatusCategory); + } + + [Test] + public void ReceivingState_OnReceiveFailureEvent_TransitionToReceiveReconnectingState() + { + //Arrange + var currentState = CreateReceivingState(); + var eventToTriggerTransition = new ReceiveFailureEvent() { }; + var expectedState = new ReceiveReconnectingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceiveReconnectingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceiveReconnectingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceiveReconnectingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceiveReconnectingState)result.State).Cursor.Timetoken); + } + + [Test] + public void ReceivingState_OnUnsubscribeAllEvent_TransitionToUnsubscribedState() + { + //Arrange + var currentState = CreateReceivingState(); + var eventToTriggerTransition = new UnsubscribeAllEvent(); + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + } + + } +} diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs new file mode 100644 index 000000000..6147eec88 --- /dev/null +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs @@ -0,0 +1,74 @@ +using NUnit.Framework; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.States; +using System.Linq; + +namespace PubnubApi.Tests.EventEngine +{ + internal class UnsubscribedStateTransition + { + [Test] + public void UnsubscribedState_OnSubscriptionChangedEvent_TransitionToHandshakingState() + { + //Arrange + var currentState = new UnsubscribedState() { Channels = new string[] { "ch1", "ch2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }; + var eventToTriggerTransition = new SubscriptionChangedEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" } + }; + var expectedState = new HandshakingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((HandshakingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((HandshakingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((HandshakingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((HandshakingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + } + + [Test] + public void UnsubscribedState_OnSubscriptionRestoreEvent_TransitionToReceivingState() + { + //Arrange + var currentState = new UnsubscribedState() { Channels = new string[] { "ch1", "ch2" }, ChannelGroups = new string[] { "cg1", "cg2" }, ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) }; + var eventToTriggerTransition = new SubscriptionRestoredEvent() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 } + }; + var expectedState = new ReceivingState() + { + Channels = new string[] { "ch1", "ch2" }, + ChannelGroups = new string[] { "cg1", "cg2" }, + Cursor = new SubscriptionCursor() { Region = 1, Timetoken = 1234567890 }, + ReconnectionConfiguration = new ReconnectionConfiguration(PNReconnectionPolicy.LINEAR, 50) + }; + + //Act + var result = currentState.Transition(eventToTriggerTransition); + + //Assert + Assert.IsInstanceOf(result.State); + CollectionAssert.AreEqual(expectedState.Channels, ((ReceivingState)result.State).Channels); + CollectionAssert.AreEqual(expectedState.ChannelGroups, ((ReceivingState)result.State).ChannelGroups); + Assert.AreEqual(expectedState.Cursor.Region, ((ReceivingState)result.State).Cursor.Region); + Assert.AreEqual(expectedState.Cursor.Timetoken, ((ReceivingState)result.State).Cursor.Timetoken); + Assert.AreEqual(expectedState.ReconnectionConfiguration.ReconnectionPolicy, ((ReceivingState)result.State).ReconnectionConfiguration.ReconnectionPolicy); + Assert.AreEqual(expectedState.ReconnectionConfiguration.MaximumReconnectionRetries, ((ReceivingState)result.State).ReconnectionConfiguration.MaximumReconnectionRetries); + } + } +} diff --git a/src/UnitTests/PubnubApi.Tests/PubnubUnitTest.cs b/src/UnitTests/PubnubApi.Tests/PubnubUnitTest.cs index 5c8bd1e51..9af6c960b 100644 --- a/src/UnitTests/PubnubApi.Tests/PubnubUnitTest.cs +++ b/src/UnitTests/PubnubApi.Tests/PubnubUnitTest.cs @@ -1,4 +1,6 @@ using PubnubApi; +using System; +using System.Collections.Generic; namespace PubNubMessaging.Tests { @@ -45,5 +47,15 @@ bool IPubnubUnitTest.IncludeUuid get; set; } + List> IPubnubUnitTest.EventTypeList + { + get; + set; + } + int IPubnubUnitTest.Attempts + { + get; + set; + } } } diff --git a/src/UnitTests/PubnubApi.Tests/WhenSubscribedToAChannel.cs b/src/UnitTests/PubnubApi.Tests/WhenSubscribedToAChannel.cs index e576aa67e..e67a02a82 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenSubscribedToAChannel.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenSubscribedToAChannel.cs @@ -184,6 +184,7 @@ private static void CommonComplexMessageSubscribeShouldReturnReceivedMessageBase { Assert.Ignore("Ignored for Server side run"); } + bool receivedSubscribedMessage = false; bool internalReceivedMessage = false; bool receivedErrorMessage = false; CustomClass publishedMessage = new CustomClass(); @@ -192,6 +193,8 @@ private static void CommonComplexMessageSubscribeShouldReturnReceivedMessageBase { PublishKey = PubnubCommon.PublishKey, SubscribeKey = PubnubCommon.SubscribeKey, + Secure = ssl, + EnableEventEngine = true, CryptoModule = new CryptoModule(new LegacyCryptor(cipherKey), null), Secure = ssl }; @@ -207,6 +210,7 @@ private static void CommonComplexMessageSubscribeShouldReturnReceivedMessageBase server.RunOnHttps(ssl); ManualResetEvent subscribeManualEvent = new ManualResetEvent(false); + ManualResetEvent subscribeMessageManualEvent = new ManualResetEvent(false); SubscribeCallback listenerSubCallack = new SubscribeCallbackExt( (o, m) => @@ -215,11 +219,11 @@ private static void CommonComplexMessageSubscribeShouldReturnReceivedMessageBase if (m != null) { Debug.WriteLine("SubscribeCallback: PNMessageResult: {0}", pubnub.JsonPluggableLibrary.SerializeToJsonString(m.Message)); - if (pubnub.JsonPluggableLibrary.SerializeToJsonString(publishedMessage) == m.Message.ToString()) + if (pubnub.JsonPluggableLibrary.SerializeToJsonString(publishedMessage) == pubnub.JsonPluggableLibrary.SerializeToJsonString(m.Message)) { internalReceivedMessage = true; } - subscribeManualEvent.Set(); + subscribeMessageManualEvent.Set(); } }, (o, p) => { @@ -305,6 +309,7 @@ private static void CommonComplexMessageSubscribeShouldReturnReceivedMessageBase if (!receivedErrorMessage) { + internalReceivedMessage = false; ManualResetEvent publishManualEvent = new ManualResetEvent(false); pubnub.Publish().Channel(channel).Message(publishedMessage) .Execute(new PNPublishResultExt((r, s) => @@ -317,6 +322,11 @@ private static void CommonComplexMessageSubscribeShouldReturnReceivedMessageBase })); publishManualEvent.WaitOne(manualResetEventWaitTimeout); + receivedMessage = internalReceivedMessage; + + subscribeMessageManualEvent.WaitOne(manualResetEventWaitTimeout); + + Thread.Sleep(1000); pubnub.Unsubscribe().Channels(new[] { channel }).Execute(); @@ -415,7 +425,8 @@ public static void ThenSubscribeShouldReturnConnectStatus() Secure = false, LogVerbosity = PNLogVerbosity.BODY, PubnubLog = new TestLog(), - NonSubscribeRequestTimeout = 120 + NonSubscribeRequestTimeout = 120, + EnableEventEngine = true }; if (PubnubCommon.PAMServerSideRun) { @@ -722,7 +733,8 @@ public static void ThenSubscriberShouldBeAbleToReceiveManyMessages() { PublishKey = PubnubCommon.PublishKey, SubscribeKey = PubnubCommon.SubscribeKey, - Secure = false + Secure = false, + EnableEventEngine = true, }; if (PubnubCommon.PAMServerSideRun) { @@ -773,7 +785,7 @@ public static void ThenSubscriberShouldBeAbleToReceiveManyMessages() manualResetEventWaitTimeout = 310 * 1000; - string channel = "hello_my_channel"; + string channel = "hello_my_channel1"; numberOfReceivedMessages = 0; string expected = "{\"t\":{\"t\":\"14839022442039237\",\"r\":7},\"m\":[]}"; diff --git a/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj b/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj index 9120ff19a..bb6001ad5 100644 --- a/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj +++ b/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj @@ -51,6 +51,15 @@ + + + + + + + + + @@ -110,6 +119,9 @@ Resource.Designer.cs + + +