From 5233fa607b8cb9e956c56b65df17395c2e639b7a Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Fri, 17 Mar 2023 15:43:56 +0530 Subject: [PATCH 01/71] Updated AsyncBridge pkg in project file --- src/Api/PubnubApi/PubnubApi.csproj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index 598646bc3..e099c4f23 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -70,8 +70,8 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. - - 0.2.0 + + 0.3.1 None @@ -89,7 +89,8 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. - + + 0.3.1 None From 990491ff21a55c2ccab48e72b6cabb5de6bcd059 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Fri, 17 Mar 2023 21:53:58 +0530 Subject: [PATCH 02/71] EventEngine WIP --- .gitignore | 7 +- .../EndPoint/PubSub/SubscribeOperation.cs | 9 + src/Api/PubnubApi/PubnubApi.csproj | 4 + src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 4 + src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 5 +- src/PNEventEngine/EffectDispatcher.cs | 30 +++ src/PNEventEngine/EventEmitter.cs | 23 +++ src/PNEventEngine/EventEngine.cs | 129 +++++++++++++ src/PNEventEngine/HandshakeEffectHandler.cs | 67 +++++++ src/PNEventEngine/IEffectHandler.cs | 18 ++ src/PNEventEngine/LICENSE.txt | 1 + src/PNEventEngine/PNEventEngine.csproj | 176 ++++++++++++++++++ src/PNEventEngine/ReceivingEffectHandler.cs | 70 +++++++ .../ReconnectingEffectHandler.cs | 30 +++ src/PNEventEngine/State.cs | 53 ++++++ src/PNEventEngine/pubnub.snk | Bin 0 -> 596 bytes src/PNEventEngineUWP/PNEventEngineUWP.csproj | 176 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 29 +++ .../Properties/PNEventEngineUWP.rd.xml | 33 ++++ src/Pubnub.sln | 168 ++++++++++++++++- 20 files changed, 1026 insertions(+), 6 deletions(-) create mode 100644 src/PNEventEngine/EffectDispatcher.cs create mode 100644 src/PNEventEngine/EventEmitter.cs create mode 100644 src/PNEventEngine/EventEngine.cs create mode 100644 src/PNEventEngine/HandshakeEffectHandler.cs create mode 100644 src/PNEventEngine/IEffectHandler.cs create mode 100644 src/PNEventEngine/LICENSE.txt create mode 100644 src/PNEventEngine/PNEventEngine.csproj create mode 100644 src/PNEventEngine/ReceivingEffectHandler.cs create mode 100644 src/PNEventEngine/ReconnectingEffectHandler.cs create mode 100644 src/PNEventEngine/State.cs create mode 100644 src/PNEventEngine/pubnub.snk create mode 100644 src/PNEventEngineUWP/PNEventEngineUWP.csproj create mode 100644 src/PNEventEngineUWP/Properties/AssemblyInfo.cs create mode 100644 src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml diff --git a/.gitignore b/.gitignore index e54aafe7b..81015bbda 100644 --- a/.gitignore +++ b/.gitignore @@ -162,10 +162,13 @@ 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 # ################## .github/.release -.idea \ No newline at end of file +.idea diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs index 3ccf66be1..03d06ae1b 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using System.Net; using System.Globalization; +using PNEventEngine; namespace PubnubApi.EndPoint { @@ -25,6 +26,7 @@ public class SubscribeOperation : PubnubCoreBase private bool presenceSubscribeEnabled; private SubscribeManager manager; private Dictionary queryParam; + private EventEngine pnEventEngine; public SubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) { @@ -34,6 +36,12 @@ public SubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary js pubnubLog = log; pubnubTelemetryMgr = telemetryManager; pubnubTokenMgr = tokenManager; + + //var effectDispatcher = new EffectDispatcher(); + //var eventEmitter = new EventEmitter(); + //var handshakeEffect = new HandshakeEffectHandler(null, eventEmitter); + //var receivingEffect = new ReceivingEffectHandler(null, eventEmitter); + //var reconnectionEffect = new ReconnectingEffectHandler(null, eventEmitter); } public SubscribeOperation Channels(string[] channels) @@ -142,6 +150,7 @@ private void Subscribe(string[] channels, string[] channelGroups, Dictionary { + //pnEventEngine = new EventEngine(null, null); manager = new SubscribeManager(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); manager.CurrentPubnubInstance(PubnubInstance); manager.MultiChannelSubscribeInit(PNOperationType.PNSubscribeOperation, channels, channelGroups, initialSubscribeUrlParams, externalQueryParam); diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index e099c4f23..07f9afe77 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -130,4 +130,8 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. + + + + diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 8330aaa89..fe0f9c53b 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -890,6 +890,10 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. + + + + - 6.2.9 + 6.2.14 9.0.1 @@ -693,6 +693,9 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. + + + 14.0 diff --git a/src/PNEventEngine/EffectDispatcher.cs b/src/PNEventEngine/EffectDispatcher.cs new file mode 100644 index 000000000..116601471 --- /dev/null +++ b/src/PNEventEngine/EffectDispatcher.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace PNEventEngine +{ + public class EffectDispatcher + { + public Dictionary effectActionMap; + public EffectDispatcher() + { + effectActionMap = new Dictionary(); + } + + public async void dispatch(EffectType effect, ExtendedState stateContext) + { + IEffectHandler? handler; + if (effectActionMap.TryGetValue(effect, out handler)) { + if (handler != null) + { + await Task.Factory.StartNew(()=> handler.Start(stateContext)).ConfigureAwait(false);; + } + } + } + + public void Register(EffectType type, IEffectHandler handler) + { + effectActionMap.Add(type, handler); + } + } +} diff --git a/src/PNEventEngine/EventEmitter.cs b/src/PNEventEngine/EventEmitter.cs new file mode 100644 index 000000000..0308a905b --- /dev/null +++ b/src/PNEventEngine/EventEmitter.cs @@ -0,0 +1,23 @@ +using System; + +namespace PNEventEngine +{ + public class EventEmitter + { + public Action? handler; + + public void RegisterHandler(Action eventHandler) + { + this.handler = eventHandler; + } + + public void emit(Event e) + { + if (handler == null) + { + throw new MissingMemberException("eventHandler is missing"); + } + this.handler(e); + } + } +} diff --git a/src/PNEventEngine/EventEngine.cs b/src/PNEventEngine/EventEngine.cs new file mode 100644 index 000000000..b64e62510 --- /dev/null +++ b/src/PNEventEngine/EventEngine.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PNEventEngine +{ + public class Event + { + public EventType Type { get; set; } + public EventPayload EventPayload { get; set; } + + public Event() + { + EventPayload = new EventPayload(); + } + } + + public class EventPayload + { + public List? Channels { get; set; } + public List? ChannelGroups { get; set; } + public string? Timetoken { get; set; } + public int? Region { get; set; } + + public Exception? exception { get; set; } + } + + + public enum EventType + { + SubscriptionChange, + HandshakeSuccess, + ReceiveSuccess, + HandshakeFailed, + ReceiveFailed, + ReconnectionFailed + } + + public class ExtendedState + { + public List Channels { get; set; } + public List ChannelGroups { get; set; } + public string Timetoken { get; set; } + public int? Region { get; set; } + public int AttemptedReconnection { get; set; } + + public ExtendedState() + { + Channels = new List(); + ChannelGroups = new List(); + Timetoken = "0"; + Region = 0; + AttemptedReconnection = 0; + } + + } + + public class EventEngine + { + public ExtendedState context; + public State? CurrentState { get; set; } + public List States { get; set; } + + public EffectDispatcher dispatcher; + + public EventEmitter emitter; + + public EventEngine(EffectDispatcher dispatcher, EventEmitter emitter) + { + 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); + States.Add(newState); + return newState; + } + + public void Transition(Event e) + { + StateType nextStateType; + if (CurrentState != null && CurrentState.transitions.TryGetValue(e.Type, out nextStateType)) { + CurrentState.Exit(); + CurrentState = this.States.Find((s) => s.Type == nextStateType); + UpdateContext(e.EventPayload); + if (CurrentState != null) + { + CurrentState.Entry(); + UpdateContext(e.EventPayload); + if (CurrentState.Effects.Count > 0) { + foreach (var effect in CurrentState.Effects) { + System.Diagnostics.Debug.WriteLine("Found effect "+ effect); + dispatcher.dispatch(effect, this.context); + } + } + } + } + } + + public void Subscribe(List channels, List? channelGroups) + { + var evnt = new Event(); + evnt.Type = EventType.SubscriptionChange; + evnt.EventPayload.Channels = channels; + if (channelGroups != null) evnt.EventPayload.ChannelGroups = channelGroups; + this.Transition(evnt); + } + + private void UpdateContext(EventPayload eventData) + { + if (eventData.Channels != null) context.Channels = eventData.Channels; + if (eventData.ChannelGroups != null) context.ChannelGroups = eventData.ChannelGroups; + if (eventData.Timetoken != null) context.Timetoken = eventData.Timetoken; + if (eventData.Region != null) context.Region = eventData.Region; + } + + public void InitialState(State state) + { + this.CurrentState = state; + } + } +} diff --git a/src/PNEventEngine/HandshakeEffectHandler.cs b/src/PNEventEngine/HandshakeEffectHandler.cs new file mode 100644 index 000000000..6bcf43867 --- /dev/null +++ b/src/PNEventEngine/HandshakeEffectHandler.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; + + +namespace PNEventEngine +{ + public class HandshakeResponse + { + [JsonProperty("t")] + public Timetoken? Timetoken { get; set; } + + [JsonProperty("m")] + public object[]? Messages { get; set; } + } + + public class Timetoken + { + [JsonProperty("t")] + public string? Timestamp { get; set; } + + [JsonProperty("r")] + public int? Region { get; set; } + + } + + public class HandshakeEffectHandler : IEffectHandler + { + EventEmitter emitter; + //HttpClient httpClient; + + //PNConfiguration pnConfig; + public HandshakeEffectHandler(Action httpRequestHandler, EventEmitter emitter) + { + this.emitter = emitter; + //httpClient = client; + //pnConfig = config; + } + public async void Start(ExtendedState context) + { + var evnt = new Event(); + await Task.Factory.StartNew(() => { }); + // TODO: Replace with Stateless Utility Methods + // TODO: Fetch Configuration from PubNub instance + try { + //var res = await httpClient.GetAsync($"https://ps.pndsn.com/v2/subscribe/sub-c-c9710928-1b7a-11e3-a0c8-02ee2ddab7fe/{String.Join(",", context.Channels.ToArray())}/0?uuid=cSharpTest&tt=0&tr=0&channel-group={String.Join(", ", context.ChannelGroups.ToArray())}"); + //var jsonResp = await res.Content.ReadAsStringAsync(); + ////LoggingMethod.WriteToLog(string.Format("HandshakeEffectHandler - {0}",jsonResp)); + //var handshakeResponse = JsonConvert.DeserializeObject(jsonResp); + //evnt.EventPayload.Timetoken = handshakeResponse.Timetoken.Timestamp; + //evnt.EventPayload.Region = handshakeResponse.Timetoken.Region; + //evnt.Type = EventType.HandshakeSuccess; + } catch (Exception ex) { + //LoggingMethod.WriteToLog(string.Format("HandshakeEffectHandler EXCEPTION - {0}",ex)); + evnt.Type = EventType.HandshakeFailed; + evnt.EventPayload.exception = ex; + } + emitter.emit(evnt); + } + public void Cancel() + { + //Console.WriteLine("Handshake can not be cancelled. Something is not right here!"); + //LoggingMethod.WriteToLog("HandshakeEffectHandler - Handshake can not be cancelled. Something is not right here!"); + } + + } +} diff --git a/src/PNEventEngine/IEffectHandler.cs b/src/PNEventEngine/IEffectHandler.cs new file mode 100644 index 000000000..c98814f59 --- /dev/null +++ b/src/PNEventEngine/IEffectHandler.cs @@ -0,0 +1,18 @@ +using System; + +namespace PNEventEngine +{ + + public enum EffectType + { + SendHandshakeRequest, // oneshot + ReceiveEventRequest, // long running + ReconnectionAttempt + } + + public interface IEffectHandler + { + void Start(ExtendedState context); + void Cancel(); + } +} diff --git a/src/PNEventEngine/LICENSE.txt b/src/PNEventEngine/LICENSE.txt new file mode 100644 index 000000000..8cc6fe770 --- /dev/null +++ b/src/PNEventEngine/LICENSE.txt @@ -0,0 +1 @@ +Please visit the site - http://www.pubnub.com/terms \ No newline at end of file diff --git a/src/PNEventEngine/PNEventEngine.csproj b/src/PNEventEngine/PNEventEngine.csproj new file mode 100644 index 000000000..c08ae82a8 --- /dev/null +++ b/src/PNEventEngine/PNEventEngine.csproj @@ -0,0 +1,176 @@ + + + + netstandard1.0;netstandard1.3;netstandard1.4;netstandard1.1;netstandard2.0;net6.0;net7.0;net35;net40;net45;net461;net48 + disable + enable + 8.0 + True + pubnub.snk + False + False + PubNub C# .NET - EventEngine + Pubnub + true + + + + + PubnubEventEngine + 1.0.0.0 + PubNub C# .NET - EventEngine + Pandu Masabathula + PubNub + LICENSE.txt + http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png + true + https://github.com/pubnub/c-sharp/ + + Initial Version. + + Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing + + PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously + PubNub 2012-2021 + false + + + + + + 0.3.1 + None + + + 1.0.2856 + None + + + 1.0.2856 + None + + + None + + + + + + + 0.3.1 + None + + + None + + + + + + + + None + + + + + + None + + + + + + None + + + + + + + None + + + + + + + None + + + + + + + None + + + + + + + None + + + + + + None + + + + + + None + + + None + + + None + + + None + + + None + + + None + + + None + + + None + + + + + + None + + + None + + + None + + + None + + + None + + + None + + + None + + + None + + + + diff --git a/src/PNEventEngine/ReceivingEffectHandler.cs b/src/PNEventEngine/ReceivingEffectHandler.cs new file mode 100644 index 000000000..d1ba3b35b --- /dev/null +++ b/src/PNEventEngine/ReceivingEffectHandler.cs @@ -0,0 +1,70 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace PNEventEngine +{ + public class ReceiveingResponse + { + [JsonProperty("t")] + public Timetoken? Timetoken { get; set; } + + [JsonProperty("m")] + public object[]? Messages { get; set; } + } + + public class ReceivingEffectHandler : IEffectHandler + { + EventEmitter emitter; + //HttpClient httpClient; + CancellationTokenSource? cancellationTokenSource; + + //PNConfiguration pnConfig; + public ReceivingEffectHandler(Action httpRequestHandler, EventEmitter emitter) + { + this.emitter = emitter; + //httpClient = client; + //pnConfig = config; + cancellationTokenSource = new CancellationTokenSource(); + } + + public async void Start(ExtendedState context) + { + await Task.Factory.StartNew(() => { }); + if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { + Cancel(); + } + cancellationTokenSource = new CancellationTokenSource(); + var evnt = new Event(); + // TODO: Replace with stateless Utility method... + try { + //var res = await httpClient.GetAsync($"https://ps.pndsn.com/v2/subscribe/sub-c-c9710928-1b7a-11e3-a0c8-02ee2ddab7fe/{String.Join(",", context.Channels.ToArray())}/0?uuid=cSharpTest&channel-group={String.Join(",", context.ChannelGroups.ToArray())}&tt={context.Timetoken}&tr={context.Region}", cancellationTokenSource.Token); + //string jsonResp = await res.Content.ReadAsStringAsync(); + ////LoggingMethod.WriteToLog(string.Format("ReceivingEffectHandler - {0}",jsonResp)); + //var receivedResponse = JsonConvert.DeserializeObject(jsonResp); + //evnt.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; + //evnt.EventPayload.Region = receivedResponse.Timetoken.Region; + //evnt.Type = EventType.ReceiveSuccess; + + //if (receivedResponse.Messages != null) + // Console.WriteLine($"Received Messages {JsonConvert.SerializeObject(receivedResponse.Messages)}"); //WIP: Define "DELIVERING" Effect. and transition + + } catch (Exception ex) { + evnt.Type = EventType.ReceiveFailed; + //LoggingMethod.WriteToLog(string.Format("ReceivingEffectHandler EXCEPTION - {0}",ex)); + evnt.EventPayload.exception = ex; + } + emitter.emit(evnt); + } + public void Cancel() + { + //Console.WriteLine("Attempting cancellation"); + //LoggingMethod.WriteToLog("ReceivingEffectHandler - Attempting cancellation"); + if (cancellationTokenSource != null) + { + cancellationTokenSource.Cancel(); + } + } + } +} diff --git a/src/PNEventEngine/ReconnectingEffectHandler.cs b/src/PNEventEngine/ReconnectingEffectHandler.cs new file mode 100644 index 000000000..a5758e953 --- /dev/null +++ b/src/PNEventEngine/ReconnectingEffectHandler.cs @@ -0,0 +1,30 @@ +using System; + +namespace PNEventEngine +{ + public class ReconnectingEffectHandler : IEffectHandler + { + EventEmitter eventEmitter; + + //PNConfiguration pnConfig; + public ReconnectingEffectHandler(Action httpRequestHandler, EventEmitter emitter) + { + this.eventEmitter = emitter; + //pnConfig = config; + } + + public void Start(ExtendedState context) // TODO: Implementation of retry getDelay() as per policy + { + var evnt = new Event(); + evnt.EventPayload.Timetoken = context.Timetoken; + evnt.EventPayload.Region = context.Region; + evnt.Type = EventType.ReceiveSuccess; + this.eventEmitter.emit(evnt); + } + + public void Cancel() + { + System.Diagnostics.Debug.WriteLine("Reconnecting Cancelled!!!"); + } + } +} diff --git a/src/PNEventEngine/State.cs b/src/PNEventEngine/State.cs new file mode 100644 index 000000000..47448415e --- /dev/null +++ b/src/PNEventEngine/State.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; + +namespace PNEventEngine +{ + + public enum StateType { Unsubscribed, Handshaking, Receiving, HandshakingFailed, ReconnectingFailed, Reconnecting }; + + public class State + { + public StateType Type { get; set; } + public Dictionary transitions; + public List Effects { get; set; } + public Func Entry { get; set; } = () => { + return true; + }; + + public Func Exit { get; set; } = () => { + return true; + }; + + public State(StateType type) + { + this.Type = type; + this.transitions = new Dictionary(); + Effects = new List(); + } + + public State On(EventType e, StateType nextState) + { + transitions.Add(e, nextState); + return this; + } + + public State OnEntry(Func entry) + { + this.Entry = entry; + return this; + } + + public State OnExit(Func exit) + { + this.Exit = exit; + return this; + } + + public State Effect(EffectType effect) + { + this.Effects.Add(effect); + return this; + } + } +} diff --git a/src/PNEventEngine/pubnub.snk b/src/PNEventEngine/pubnub.snk new file mode 100644 index 0000000000000000000000000000000000000000..ab6f98e6ccbe10048ac6dfc9922832a5c4b39a22 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096=h*pb2CXpvv;Js;9b9kQs|3b=ComT{N ziUL7K9M}p!WAqV04_SQXqzw;R;bq54Xnywpy`2cq;aP9P6vK}{hq(oAm@7=O7(xoP zY}{Hc&8AbMXN0npkl4STLH<s^7fZ z@G104xb8*?e}bGrE&T#EGhb0ocj6KeeXb$DF{GaDvCq4&h7(OV(!{x~o|bHSlV7^| zYGMl`=Lx$CL!#xY((Qw3Urdawedl{1sf}?e9lj{n(#+}o*+?P|*NTx5C|O{NdRMOo z87;7|v4snR2zBjV|4Qh0`ku#5QjgMyS*v<3vRA(U%7OKwYjO064<=cZ28s>#-RxXL4I#C2%<%<-zIIrTY)8c*(A?p9hA)oTMXkCTlb(Wf5axkzrW%&T zoJj}GHDWELO-QEm6L}-9Q=xdCRkmb#< zm+&3vUMMt5!+Rx)1B9~r#!8#o5WY*jh0;VT@ygH53);jOQ6>PZOhQO1K6(G5{3|HU iSa<~)<|H!5yTV$GoUpH<7?8K2GQo*km!(;|inuqU + + + + Debug + AnyCPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD} + Library + Properties + PNEventEngineUWP + PNEventEngineUWP + en-US + UAP + 10.0.22000.0 + 10.0.10586.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + PackageReference + disable + enable + 8.0 + + + + + EffectDispatcher.cs + + + EventEmitter.cs + + + EventEngine.cs + + + HandshakeEffectHandler.cs + + + IEffectHandler.cs + + + ReceivingEffectHandler.cs + + + ReconnectingEffectHandler.cs + + + State.cs + + + + + + + None + + + + + + 6.2.14 + + + + 14.0 + + + + \ No newline at end of file diff --git a/src/PNEventEngineUWP/Properties/AssemblyInfo.cs b/src/PNEventEngineUWP/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d6c1417c8 --- /dev/null +++ b/src/PNEventEngineUWP/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PNEventEngineUWP")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PNEventEngineUWP")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml b/src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml new file mode 100644 index 000000000..e8b9c7f98 --- /dev/null +++ b/src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/src/Pubnub.sln b/src/Pubnub.sln index 13b6bc8a2..6848991b6 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,24 +27,32 @@ 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PNEventEngine", "PNEventEngine\PNEventEngine.csproj", "{611832BF-3571-4186-B28F-DE4864DE60EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PNEventEngineUWP", "PNEventEngineUWP\PNEventEngineUWP.csproj", "{E47B5226-5A59-484B-BAA6-19BBD7C5BACD}" EndProject 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 @@ -53,6 +61,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 @@ -61,6 +71,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 @@ -69,6 +81,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 @@ -77,6 +91,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 @@ -85,6 +101,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 @@ -93,6 +111,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 @@ -101,6 +121,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 @@ -109,6 +131,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 @@ -117,6 +141,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 @@ -125,6 +151,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 @@ -133,6 +161,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 @@ -141,6 +171,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 @@ -149,6 +181,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 @@ -157,6 +191,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 @@ -165,6 +201,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 @@ -173,6 +211,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 @@ -181,6 +221,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 @@ -189,6 +231,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 @@ -197,6 +241,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 @@ -205,6 +251,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 @@ -213,6 +261,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 @@ -221,6 +271,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 @@ -229,6 +281,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 @@ -237,6 +291,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 @@ -245,6 +301,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 @@ -253,6 +311,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 @@ -261,6 +321,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 @@ -269,6 +331,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 @@ -277,6 +341,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 @@ -285,6 +351,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 @@ -293,6 +361,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 @@ -301,6 +371,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 @@ -309,6 +381,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 @@ -317,6 +391,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 @@ -325,6 +401,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 @@ -333,10 +411,92 @@ 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 {15805B6C-C474-4DD7-BD7F-150A7EA23F5C}.Release|x86.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|ARM.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|ARM.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|ARM64.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|x64.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|x64.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|x86.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug_Ubuntu|x86.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|ARM.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|ARM.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|ARM64.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|x64.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|x64.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|x86.ActiveCfg = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Debug|x86.Build.0 = Debug|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|Any CPU.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|ARM.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|ARM.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|ARM64.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|ARM64.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|x64.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|x64.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|x86.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release_Ubuntu|x86.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|Any CPU.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|ARM.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|ARM.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|ARM64.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|ARM64.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|x64.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|x64.Build.0 = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|x86.ActiveCfg = Release|Any CPU + {611832BF-3571-4186-B28F-DE4864DE60EC}.Release|x86.Build.0 = Release|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|ARM.ActiveCfg = Debug|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|ARM.Build.0 = Debug|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|ARM64.ActiveCfg = Debug|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|ARM64.Build.0 = Debug|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|x64.ActiveCfg = Debug|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|x64.Build.0 = Debug|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|x86.ActiveCfg = Debug|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug_Ubuntu|x86.Build.0 = Debug|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|ARM.ActiveCfg = Debug|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|ARM.Build.0 = Debug|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|ARM64.Build.0 = Debug|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|x64.ActiveCfg = Debug|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|x64.Build.0 = Debug|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|x86.ActiveCfg = Debug|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Debug|x86.Build.0 = Debug|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|Any CPU.ActiveCfg = Release|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|ARM.ActiveCfg = Release|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|ARM.Build.0 = Release|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|ARM64.ActiveCfg = Release|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|ARM64.Build.0 = Release|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|x64.ActiveCfg = Release|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|x64.Build.0 = Release|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|x86.ActiveCfg = Release|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release_Ubuntu|x86.Build.0 = Release|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|Any CPU.Build.0 = Release|Any CPU + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|ARM.ActiveCfg = Release|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|ARM.Build.0 = Release|ARM + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|ARM64.ActiveCfg = Release|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|ARM64.Build.0 = Release|ARM64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|x64.ActiveCfg = Release|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|x64.Build.0 = Release|x64 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|x86.ActiveCfg = Release|x86 + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -351,6 +511,8 @@ Global {5ACBD3DD-B120-4F8C-91EB-D4D8F83E9F2C} = {D2051EEF-B0DA-43C4-8569-4D48FE76A5CB} {C1449A27-2C29-40FF-BAD4-521BDFD323EB} = {D2051EEF-B0DA-43C4-8569-4D48FE76A5CB} {15805B6C-C474-4DD7-BD7F-150A7EA23F5C} = {D2051EEF-B0DA-43C4-8569-4D48FE76A5CB} + {611832BF-3571-4186-B28F-DE4864DE60EC} = {F101EAB9-89CA-4ACB-A72B-0660F4C9CC38} + {E47B5226-5A59-484B-BAA6-19BBD7C5BACD} = {F101EAB9-89CA-4ACB-A72B-0660F4C9CC38} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A640EAE8-CEFD-4AFB-86DE-4112005EB515} From ac1591fc8c52dc66c13508600cea41fabffb5ed2 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 20 Mar 2023 22:24:08 +0530 Subject: [PATCH 03/71] EventEngine Handshake and Receiving --- .../EndPoint/PubSub/SubscribeManager2.cs | 1356 +++++++++ .../EndPoint/PubSub/SubscribeOperation.cs | 9 - .../EndPoint/PubSub/SubscribeOperation2.cs | 337 +++ .../PubnubApi/EventEngine/EffectDispatcher.cs | 31 + src/Api/PubnubApi/EventEngine/EventEmitter.cs | 23 + src/Api/PubnubApi/EventEngine/EventEngine.cs | 129 + .../EventEngine/HandshakeEffectHandler.cs | 90 + .../PubnubApi/EventEngine/IEffectHandler.cs | 18 + .../EventEngine/ReceivingEffectHandler.cs | 158 ++ .../EventEngine/ReconnectingEffectHandler.cs | 30 + src/Api/PubnubApi/EventEngine/State.cs | 53 + src/Api/PubnubApi/PNConfiguration.cs | 3 + src/Api/PubnubApi/Pubnub.cs | 7 + src/Api/PubnubApi/PubnubApi.csproj | 3 +- src/Api/PubnubApi/PubnubCoreBase2.cs | 2502 +++++++++++++++++ src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 17 +- src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 22 +- src/PNEventEngine/EffectDispatcher.cs | 3 +- src/PNEventEngine/EventEngine.cs | 28 +- src/PNEventEngine/HandshakeEffectHandler.cs | 56 +- src/Pubnub.sln | 86 - .../WhenSubscribedToAChannel.cs | 12 +- 22 files changed, 4835 insertions(+), 138 deletions(-) create mode 100644 src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs create mode 100644 src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs create mode 100644 src/Api/PubnubApi/EventEngine/EffectDispatcher.cs create mode 100644 src/Api/PubnubApi/EventEngine/EventEmitter.cs create mode 100644 src/Api/PubnubApi/EventEngine/EventEngine.cs create mode 100644 src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/IEffectHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/State.cs create mode 100644 src/Api/PubnubApi/PubnubCoreBase2.cs diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs new file mode 100644 index 000000000..1340b824a --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs @@ -0,0 +1,1356 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using PubnubApi.Interface; +using System.Net; +using System.Threading.Tasks; +using System.Globalization; + + +namespace PubnubApi.EndPoint +{ + internal class SubscribeManager2 : PubnubCoreBase2, IDisposable + { + private static ConcurrentDictionary config { get; } = new ConcurrentDictionary(); + private static IJsonPluggableLibrary jsonLibrary; + private static IPubnubUnitTest unit; + private static IPubnubLog pubnubLog; + private static EndPoint.TelemetryManager pubnubTelemetryMgr; + + private static Timer SubscribeHeartbeatCheckTimer; + private Timer multiplexExceptionTimer; + private Dictionary customQueryParam; + + public SubscribeManager2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) + { + config.AddOrUpdate(instance.InstanceId, pubnubConfig, (k, o) => pubnubConfig); + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + } + +#pragma warning disable + internal void MultiChannelUnSubscribeAll(PNOperationType type, Dictionary externalQueryParam) +#pragma warning restore + { + //Retrieve the current channels already subscribed previously and terminate them + string[] currentChannels = (MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelSubscribe[PubnubInstance.InstanceId] != null) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.ToArray() : null; + string[] currentChannelGroups = (MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelGroupSubscribe[PubnubInstance.InstanceId] != null) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.ToArray() : null; + + if (currentChannels != null && currentChannels.Length >= 0) + { + string multiChannelName = (currentChannels.Length > 0) ? string.Join(",", currentChannels.OrderBy(x => x).ToArray()) : ","; + string multiChannelGroupName = (currentChannelGroups.Length > 0) ? string.Join(",", currentChannelGroups.OrderBy(x => x).ToArray()) : ""; + + Task.Factory.StartNew(() => + { + if (ChannelRequest[PubnubInstance.InstanceId].ContainsKey(multiChannelName)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Aborting previous subscribe/presence requests having channel(s)={1}; channelgroup(s)={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + + HttpWebRequest webRequest; + ChannelRequest[PubnubInstance.InstanceId].TryGetValue(multiChannelName, out webRequest); + ChannelRequest[PubnubInstance.InstanceId].TryUpdate(multiChannelName, null, webRequest); + + HttpWebRequest removedRequest; + bool removedChannel = ChannelRequest[PubnubInstance.InstanceId].TryRemove(multiChannelName, out removedRequest); + if (removedChannel) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Success to remove channel(s)={1}; channelgroup(s)={2} from _channelRequest (MultiChannelUnSubscribeInit).", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Unable to remove channel(s)={1}; channelgroup(s)={2} from _channelRequest (MultiChannelUnSubscribeInit).", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + if (webRequest != null) + { + TerminatePendingWebRequest(webRequest); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Unable to capture channel(s)={1}; channelgroup(s)={2} from _channelRequest to abort request.", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + + if (type == PNOperationType.PNUnsubscribeOperation && config.ContainsKey(PubnubInstance.InstanceId) && !config[PubnubInstance.InstanceId].SuppressLeaveEvents) + { + //just fire leave() event to REST API for safeguard + string channelsJsonState = BuildJsonUserState(currentChannels, currentChannelGroups, false); + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config[PubnubInstance.InstanceId], jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); + + Uri request = urlBuilder.BuildMultiChannelLeaveRequest("GET", "", currentChannels, currentChannelGroups, channelsJsonState, externalQueryParam); + + RequestState requestState = new RequestState(); + requestState.Channels = currentChannels; + requestState.ChannelGroups = currentChannelGroups; + requestState.ResponseType = PNOperationType.Leave; + requestState.Reconnect = false; + + UrlProcessRequest(request, requestState, false).ContinueWith(r => + { + MultiChannelSubscribe[PubnubInstance.InstanceId].Clear(); + MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Clear(); + }); + } + } + + } + + internal void MultiChannelUnSubscribeInit(PNOperationType type, string channel, string channelGroup, Dictionary externalQueryParam) + { + List validChannels = new List(); + List validChannelGroups = new List(); + + try + { + this.customQueryParam = externalQueryParam; + + if (PubnubInstance == null) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, PubnubInstance is null. exiting MultiChannelUnSubscribeInit", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + return; + } + + if (!MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + MultiChannelSubscribe.GetOrAdd(PubnubInstance.InstanceId, new ConcurrentDictionary()); + } + if (!MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + MultiChannelGroupSubscribe.GetOrAdd(PubnubInstance.InstanceId, new ConcurrentDictionary()); + } + + string[] rawChannels = (channel != null && channel.Trim().Length > 0) ? channel.Split(',') : new string[] { }; + string[] rawChannelGroups = (channelGroup != null && channelGroup.Trim().Length > 0) ? channelGroup.Split(',') : new string[] { }; + + if (rawChannels.Length > 0) + { + for (int index = 0; index < rawChannels.Length; index++) + { + if (rawChannels[index].Trim().Length > 0) + { + string channelName = rawChannels[index].Trim(); + if (string.IsNullOrEmpty(channelName)) + { + continue; + } + + if (config.ContainsKey(PubnubInstance.InstanceId) && MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelSubscribe[PubnubInstance.InstanceId] != null && !MultiChannelSubscribe[PubnubInstance.InstanceId].ContainsKey(channelName)) + { + PNStatus status = new StatusBuilder(config[PubnubInstance.InstanceId], jsonLibrary).CreateStatusResponse(PNOperationType.PNUnsubscribeOperation, PNStatusCategory.PNUnexpectedDisconnectCategory, null, (int)HttpStatusCode.NotFound, null); + if (!status.AffectedChannels.Contains(channelName)) + { + status.AffectedChannels.Add(channelName); + } + Announce(status); + } + else + { + validChannels.Add(channelName); + string presenceChannelName = string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", channelName); + if (MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelSubscribe[PubnubInstance.InstanceId] != null && MultiChannelSubscribe[PubnubInstance.InstanceId].ContainsKey(presenceChannelName)) + { + validChannels.Add(presenceChannelName); + } + } + } + } + } + + if (rawChannelGroups.Length > 0) + { + for (int index = 0; index < rawChannelGroups.Length; index++) + { + if (rawChannelGroups[index].Trim().Length > 0) + { + string channelGroupName = rawChannelGroups[index].Trim(); + if (string.IsNullOrEmpty(channelGroupName)) + { + continue; + } + + if (config.ContainsKey(PubnubInstance.InstanceId) && MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelGroupSubscribe[PubnubInstance.InstanceId] != null && !MultiChannelGroupSubscribe[PubnubInstance.InstanceId].ContainsKey(channelGroupName)) + { + PNStatus status = new StatusBuilder(config[PubnubInstance.InstanceId], jsonLibrary).CreateStatusResponse(PNOperationType.PNUnsubscribeOperation, PNStatusCategory.PNUnexpectedDisconnectCategory, null, (int)HttpStatusCode.NotFound, null); + if (!status.AffectedChannelGroups.Contains(channelGroupName)) + { + status.AffectedChannelGroups.Add(channelGroupName); + } + Announce(status); + } + else + { + validChannelGroups.Add(channelGroupName); + string presenceChannelGroupName = string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", channelGroupName); + if (MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelGroupSubscribe[PubnubInstance.InstanceId] != null && MultiChannelGroupSubscribe[PubnubInstance.InstanceId].ContainsKey(presenceChannelGroupName)) + { + validChannelGroups.Add(presenceChannelGroupName); + } + } + } + } + } + + if (validChannels.Count > 0 || validChannelGroups.Count > 0) + { + //Retrieve the current channels already subscribed previously and terminate them + string[] currentChannels = MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.ToArray(); + string[] currentChannelGroups = MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.ToArray(); + + if (currentChannels != null && currentChannels.Length >= 0) + { + string multiChannelName = (currentChannels.Length > 0) ? string.Join(",", currentChannels.OrderBy(x => x).ToArray()) : ","; + string multiChannelGroupName = (currentChannelGroups.Length > 0) ? string.Join(",", currentChannelGroups.OrderBy(x => x).ToArray()) : ""; + + Task.Factory.StartNew(() => + { + if (ChannelRequest[PubnubInstance.InstanceId].ContainsKey(multiChannelName)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Aborting previous subscribe/presence requests having channel(s)={1}; channelgroup(s)={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + + HttpWebRequest webRequest; + ChannelRequest[PubnubInstance.InstanceId].TryGetValue(multiChannelName, out webRequest); + ChannelRequest[PubnubInstance.InstanceId].TryUpdate(multiChannelName, null, webRequest); + + HttpWebRequest removedRequest; + bool removedChannel = ChannelRequest[PubnubInstance.InstanceId].TryRemove(multiChannelName, out removedRequest); + if (removedChannel) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Success to remove channel(s)={1}; channelgroup(s)={2} from _channelRequest (MultiChannelUnSubscribeInit).", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Unable to remove channel(s)={1}; channelgroup(s)={2} from _channelRequest (MultiChannelUnSubscribeInit).", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + if (webRequest != null) + { + TerminatePendingWebRequest(webRequest); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Unable to capture channel(s)={1}; channelgroup(s)={2} from _channelRequest to abort request.", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannelName, multiChannelGroupName), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + + if (type == PNOperationType.PNUnsubscribeOperation && config.ContainsKey(PubnubInstance.InstanceId)) + { + //just fire leave() event to REST API for safeguard + string channelsJsonState = BuildJsonUserState(validChannels.ToArray(), validChannelGroups.ToArray(), false); + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config[PubnubInstance.InstanceId], jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); + + Uri request = urlBuilder.BuildMultiChannelLeaveRequest("GET", "", validChannels.ToArray(), validChannelGroups.ToArray(), channelsJsonState, externalQueryParam); + + RequestState requestState = new RequestState(); + requestState.Channels = new [] { channel }; + requestState.ChannelGroups = new [] { channelGroup }; + requestState.ResponseType = PNOperationType.Leave; + requestState.Reconnect = false; + + UrlProcessRequest(request, requestState, false).ContinueWith(r => { }, TaskContinuationOptions.ExecuteSynchronously).Wait(); + } + } + + Dictionary originalMultiChannelSubscribe = null; + Dictionary originalMultiChannelGroupSubscribe = null; + if (PubnubInstance != null && MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + originalMultiChannelSubscribe = MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0 ? MultiChannelSubscribe[PubnubInstance.InstanceId].ToDictionary(kvp => kvp.Key, kvp => kvp.Value) : null; + } + if (PubnubInstance != null && MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + originalMultiChannelGroupSubscribe = MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0 ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].ToDictionary(kvp => kvp.Key, kvp => kvp.Value) : null; + } + + PNStatus successStatus = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(PNOperationType.PNUnsubscribeOperation, PNStatusCategory.PNDisconnectedCategory, null, (int)HttpStatusCode.OK, null); + PNStatus failStatus = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(PNOperationType.PNUnsubscribeOperation, PNStatusCategory.PNDisconnectedCategory, null, (int)HttpStatusCode.NotFound, new PNException("Unsubscribe Error. Please retry unsubscribe operation")); + bool successExist = false; + bool failExist = false; + + //Remove the valid channels from subscribe list for unsubscribe + for (int index = 0; index < validChannels.Count; index++) + { + long timetokenValue; + string channelToBeRemoved = validChannels[index]; + bool unsubscribeStatus = false; + if (PubnubInstance != null && MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + unsubscribeStatus = MultiChannelSubscribe[PubnubInstance.InstanceId].TryRemove(channelToBeRemoved, out timetokenValue); + } + if (channelToBeRemoved.Contains("-pnpres")) + { + continue; //Do not send status for -pnpres channels + } + if (unsubscribeStatus) + { + successExist = true; + if (!successStatus.AffectedChannels.Contains(channelToBeRemoved)) + { + successStatus.AffectedChannels.Add(channelToBeRemoved); + } + + base.DeleteLocalChannelUserState(channelToBeRemoved); + } + else + { + failExist = true; + if (!failStatus.AffectedChannels.Contains(channelToBeRemoved)) + { + failStatus.AffectedChannels.Add(channelToBeRemoved); + } + } + } + for (int index = 0; index < validChannelGroups.Count; index++) + { + long timetokenValue; + string channelGroupToBeRemoved = validChannelGroups[index]; + bool unsubscribeStatus = false; + if (PubnubInstance != null && MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + unsubscribeStatus = MultiChannelGroupSubscribe[PubnubInstance.InstanceId].TryRemove(channelGroupToBeRemoved, out timetokenValue); + } + if (channelGroupToBeRemoved.Contains("-pnpres")) + { + continue; //Do not send status for -pnpres channel-groups + } + if (unsubscribeStatus) + { + successExist = true; + if (!successStatus.AffectedChannelGroups.Contains(channelGroupToBeRemoved)) + { + successStatus.AffectedChannelGroups.Add(channelGroupToBeRemoved); + } + + base.DeleteLocalChannelGroupUserState(channelGroupToBeRemoved); + } + else + { + failExist = true; + if (!failStatus.AffectedChannelGroups.Contains(channelGroupToBeRemoved)) + { + failStatus.AffectedChannelGroups.Add(channelGroupToBeRemoved); + } + } + } + + if (successExist && PubnubInstance != null) + { + Announce(successStatus); + } + + if (failExist && PubnubInstance != null) + { + Announce(failStatus); + } + + //Get all the channels + string[] channels = new string[] { }; + string[] channelGroups = new string[] { }; + + if (PubnubInstance != null && MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + channels = MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.ToArray(); + //Check any chained subscribes while unsubscribe + for (int keyIndex = 0; keyIndex < MultiChannelSubscribe[PubnubInstance.InstanceId].Count; keyIndex++) + { + KeyValuePair kvp = MultiChannelSubscribe[PubnubInstance.InstanceId].ElementAt(keyIndex); + if (originalMultiChannelSubscribe != null && !originalMultiChannelSubscribe.ContainsKey(kvp.Key)) + { + return; + } + } + } + + if (PubnubInstance != null && MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + channelGroups = MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.ToArray(); + for (int keyIndex = 0; keyIndex < MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count; keyIndex++) + { + KeyValuePair kvp = MultiChannelGroupSubscribe[PubnubInstance.InstanceId].ElementAt(keyIndex); + if (originalMultiChannelGroupSubscribe != null && !originalMultiChannelGroupSubscribe.ContainsKey(kvp.Key)) + { + return; + } + } + } + + channels = (channels != null) ? channels : new string[] { }; + channelGroups = (channelGroups != null) ? channelGroups : new string[] { }; + + if (channels.Length > 0 || channelGroups.Length > 0) + { + string multiChannel = (channels.Length > 0) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ","; + + RequestState state = new RequestState(); + ChannelRequest[PubnubInstance.InstanceId].AddOrUpdate(multiChannel, state.Request, (key, oldValue) => state.Request); + + ResetInternetCheckSettings(channels, channelGroups); + + + //Continue with any remaining channels for subscribe/presence + MultiChannelSubscribeRequest(PNOperationType.PNSubscribeOperation, channels, channelGroups, 0, 0, false, null, this.customQueryParam); + } + else + { + if (PresenceHeartbeatTimer != null) + { + // Stop the presence heartbeat timer if there are no channels subscribed + PresenceHeartbeatTimer.Dispose(); + PresenceHeartbeatTimer = null; + } + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, All channels are Unsubscribed. Further subscription was stopped", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + } + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager=> MultiChannelUnSubscribeInit \n channel(s)={1} \n cg(s)={2} \n Exception Details={3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), string.Join(",", validChannels.OrderBy(x => x).ToArray()), string.Join(",", validChannelGroups.OrderBy(x => x).ToArray()), ex), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + } + + internal async Task HandshakeRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + { + string json = ""; + + try + { + string channelsJsonState = BuildJsonUserState(channels, channelGroups, false); + + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config[PubnubInstance.InstanceId], jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); + 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 + + await UrlProcessRequest(request, pubnubRequestState, false).ContinueWith(r => + { + json = r.Result.Item1; + }, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false); + } + 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.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + return json; + } + + private void MultiChannelSubscribeRequest(PNOperationType type, string[] channels, string[] channelGroups, object timetoken, int region, bool reconnect, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + { + if (!config.ContainsKey(PubnubInstance.InstanceId)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, InstanceId Not Available. Exiting MultiChannelSubscribeRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture)), PNLogVerbosity.BODY); + return; + } + if (SubscribeDisconnected[PubnubInstance.InstanceId]) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeDisconnected. Exiting MultiChannelSubscribeRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + return; + } + + //Exit if the channel is unsubscribed + if (MultiChannelSubscribe != null && MultiChannelSubscribe[PubnubInstance.InstanceId].Count <= 0 && MultiChannelGroupSubscribe != null && MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count <= 0) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Zero channels/channelGroups. Further subscription was stopped", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + return; + } + + string multiChannel = (channels != null && channels.Length > 0) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ","; + string multiChannelGroup = (channelGroups != null && channelGroups.Length > 0) ? string.Join(",", channelGroups.OrderBy(x => x).ToArray()) : ""; + + bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, type, null, channels, channelGroups); + + if (!networkConnection) + { + ConnectionErrors++; + UpdatePubnubNetworkTcpCheckIntervalInSeconds(); + ChannelInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(multiChannel, networkConnection, (key, oldValue) => networkConnection); + ChannelGroupInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(multiChannelGroup, networkConnection, (key, oldValue) => networkConnection); + } + + bool channelInternetFlag; + bool channelGroupInternetFlag; + if (((ChannelInternetStatus[PubnubInstance.InstanceId].ContainsKey(multiChannel) && ChannelInternetStatus[PubnubInstance.InstanceId].TryGetValue(multiChannel, out channelInternetFlag) && !channelInternetFlag) + || (multiChannelGroup != "" && ChannelGroupInternetStatus[PubnubInstance.InstanceId].ContainsKey(multiChannelGroup) && ChannelGroupInternetStatus[PubnubInstance.InstanceId].TryGetValue(multiChannelGroup, out channelGroupInternetFlag) && !channelGroupInternetFlag)) + && PubnetSystemActive) + { + if (ReconnectNetworkIfOverrideTcpKeepAlive(type, channels, channelGroups, timetoken, region, networkConnection)) + { + return; + } + } + + if (!ChannelRequest.ContainsKey(PubnubInstance.InstanceId) || (!multiChannel.Equals(",", StringComparison.OrdinalIgnoreCase) && !ChannelRequest[PubnubInstance.InstanceId].ContainsKey(multiChannel))) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, PubnubInstance.InstanceId NOT matching", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + return; + } + + + // Begin recursive subscribe + RequestState pubnubRequestState = null; + try + { + this.customQueryParam = externalQueryParam; + RegisterPresenceHeartbeatTimer(channels, channelGroups); + + long lastTimetoken = 0; + long minimumTimetoken1 = (MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Min(token => token.Value) : 0; + long minimumTimetoken2 = (MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Min(token => token.Value) : 0; + long minimumTimetoken = Math.Max(minimumTimetoken1, minimumTimetoken2); + + long maximumTimetoken1 = (MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Max(token => token.Value) : 0; + long maximumTimetoken2 = (MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Max(token => token.Value) : 0; + long maximumTimetoken = Math.Max(maximumTimetoken1, maximumTimetoken2); + + + if (minimumTimetoken == 0 || reconnect || UserIdChanged[PubnubInstance.InstanceId]) + { + lastTimetoken = 0; + UserIdChanged.AddOrUpdate(PubnubInstance.InstanceId, false, (k, o) => false); + } + else + { + if (LastSubscribeTimetoken[PubnubInstance.InstanceId] == maximumTimetoken) + { + lastTimetoken = maximumTimetoken; + } + else + { + lastTimetoken = LastSubscribeTimetoken[PubnubInstance.InstanceId]; + } + } + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Building request for channel(s)={1}, channelgroup(s)={2} with timetoken={3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), multiChannel, multiChannelGroup, lastTimetoken), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + // Build URL + string channelsJsonState = BuildJsonUserState(channels, channelGroups, false); + config[PubnubInstance.InstanceId].UserId = CurrentUserId[PubnubInstance.InstanceId]; // to make sure we capture if UUID is changed + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config[PubnubInstance.InstanceId], jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); + + Uri request = urlBuilder.BuildMultiChannelSubscribeRequest("GET", "", channels, channelGroups, (Convert.ToInt64(timetoken.ToString(), CultureInfo.InvariantCulture) == 0) ? Convert.ToInt64(timetoken.ToString(), CultureInfo.InvariantCulture) : lastTimetoken, region, channelsJsonState, initialSubscribeUrlParams, externalQueryParam); + + pubnubRequestState = new RequestState(); + pubnubRequestState.Channels = channels; + pubnubRequestState.ChannelGroups = channelGroups; + pubnubRequestState.ResponseType = type; + pubnubRequestState.Reconnect = reconnect; + pubnubRequestState.Timetoken = Convert.ToInt64(timetoken.ToString(), CultureInfo.InvariantCulture); + pubnubRequestState.Region = region; + pubnubRequestState.TimeQueued = DateTime.Now; + + // Wait for message + string json = ""; + UrlProcessRequest(request, pubnubRequestState, false).ContinueWith(r => + { + json = r.Result.Item1; + }, TaskContinuationOptions.ExecuteSynchronously).Wait(); + if (!string.IsNullOrEmpty(json)) + { + string subscribedChannels = (MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.OrderBy(x=>x).Aggregate((x, y) => x + "," + y) : ""; + string currentChannels = (channels != null && channels.Length > 0) ? channels.OrderBy(x => x).Aggregate((x, y) => x + "," + y) : ""; + + string subscribedChannelGroups = (MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.OrderBy(x => x).Aggregate((x, y) => x + "," + y) : ""; + string currentChannelGroups = (channelGroups != null && channelGroups.Length > 0) ? channelGroups.OrderBy(x => x).Aggregate((x, y) => x + "," + y) : ""; + + if (subscribedChannels.Equals(currentChannels, StringComparison.OrdinalIgnoreCase) && subscribedChannelGroups.Equals(currentChannelGroups, StringComparison.OrdinalIgnoreCase)) + { + List result = ProcessJsonResponse(pubnubRequestState, json); + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, result count of ProcessJsonResponse = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), (result != null) ? result.Count : -1), config[PubnubInstance.InstanceId].LogVerbosity); + + ProcessResponseCallbacks(result, pubnubRequestState); + + if ((pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence) && (result != null) && (result.Count > 0)) + { + long jsonTimetoken = GetTimetokenFromMultiplexResult(result); + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, jsonTimetoken = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonTimetoken), config[PubnubInstance.InstanceId].LogVerbosity); + + if (jsonTimetoken > 0) + { + if (pubnubRequestState.Channels != null) + { + foreach (string currentChannel in pubnubRequestState.Channels) + { + MultiChannelSubscribe[PubnubInstance.InstanceId].AddOrUpdate(currentChannel, jsonTimetoken, (key, oldValue) => jsonTimetoken); + } + } + if (pubnubRequestState.ChannelGroups != null && pubnubRequestState.ChannelGroups.Length > 0) + { + foreach (string currentChannelGroup in pubnubRequestState.ChannelGroups) + { + MultiChannelGroupSubscribe[PubnubInstance.InstanceId].AddOrUpdate(currentChannelGroup, jsonTimetoken, (key, oldValue) => jsonTimetoken); + } + } + } + } + + if (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation) + { + MultiplexInternalCallback(pubnubRequestState.ResponseType, result); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, condition failed for subscribedChannels == currentChannels && subscribedChannelGroups == currentChannelGroups", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, subscribedChannels = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), subscribedChannels), config[PubnubInstance.InstanceId].LogVerbosity); + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, currentChannels = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), currentChannels), config[PubnubInstance.InstanceId].LogVerbosity); + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, subscribedChannelGroups = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), subscribedChannelGroups), config[PubnubInstance.InstanceId].LogVerbosity); + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, currentChannelGroups = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), currentChannelGroups), config[PubnubInstance.InstanceId].LogVerbosity); + } + + } + else + { + if (multiplexExceptionTimer != null) + { + multiplexExceptionTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + ConnectionErrors++; + UpdatePubnubNetworkTcpCheckIntervalInSeconds(); + multiplexExceptionTimer = new Timer(new TimerCallback(MultiplexExceptionHandlerTimerCallback), pubnubRequestState, + (-1 == PubnubNetworkTcpCheckIntervalInSeconds) ? Timeout.Infinite : PubnubNetworkTcpCheckIntervalInSeconds * 1000, + Timeout.Infinite); + } + } + catch (Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} method:_subscribe \n channel={1} \n timetoken={2} \n Exception Details={3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), string.Join(",", channels.OrderBy(x => x).ToArray()), timetoken, ex), config[PubnubInstance.InstanceId].LogVerbosity); + + PNStatusCategory errorCategory = PNStatusCategoryHelper.GetPNStatusCategory(ex); + PNStatus status = new StatusBuilder(config[PubnubInstance.InstanceId], jsonLibrary).CreateStatusResponse(type, errorCategory, pubnubRequestState, (int)HttpStatusCode.NotFound, new PNException(ex)); + if (channels != null && channels.Length > 0) + { + status.AffectedChannels.AddRange(channels); + } + + if (channelGroups != null && channelGroups.Length > 0) + { + status.AffectedChannels.AddRange(channelGroups); + } + + Announce(status); + + MultiChannelSubscribeRequest(type, channels, channelGroups, LastSubscribeTimetoken[PubnubInstance.InstanceId], LastSubscribeRegion[PubnubInstance.InstanceId], false, null, externalQueryParam); + } + } + + private void MultiplexExceptionHandlerTimerCallback(object state) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} MultiplexExceptionHandlerTimerCallback", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + RequestState currentState = state as RequestState; + if (currentState != null) + { + MultiplexExceptionHandler(currentState.ResponseType, currentState.Channels, currentState.ChannelGroups, false); + } + } + + private void MultiplexExceptionHandler(PNOperationType type, string[] channels, string[] channelGroups, bool resumeOnReconnect) + { + List result = new List(); + result.Add("0"); + if (resumeOnReconnect || LastSubscribeTimetoken == null || !LastSubscribeTimetoken.ContainsKey(PubnubInstance.InstanceId)) + { + result.Add(0); //send 0 time token to enable presence event + } + else + { + long lastTT = LastSubscribeTimetoken[PubnubInstance.InstanceId]; //get last timetoken of the current instance + int lastRegionId = (LastSubscribeRegion != null && LastSubscribeRegion.ContainsKey(PubnubInstance.InstanceId)) ? LastSubscribeRegion[PubnubInstance.InstanceId] : 0; //get last region of the current instance + + Dictionary dictTimetokenAndRegion = new Dictionary(); + dictTimetokenAndRegion.Add("t", lastTT); + dictTimetokenAndRegion.Add("r", lastRegionId); + + Dictionary dictEnvelope = new Dictionary(); + dictEnvelope.Add("t", dictTimetokenAndRegion); + result.Add(dictEnvelope); + } + + if (channelGroups != null && channelGroups.Length > 0) + { + result.Add(channelGroups); + } + result.Add(channels); //send channel name + + MultiplexInternalCallback(type, result); + } + + private void MultiplexInternalCallback(PNOperationType type, object multiplexResult) + { + List message = multiplexResult as List; + string[] channels = null; + string[] channelGroups = null; + if (message != null && message.Count >= 3) + { + if (message[message.Count - 1] is string[]) + { + channels = message[message.Count - 1] as string[]; + } + else + { + channels = message[message.Count - 1].ToString().Split(','); + } + + if (channels.Length == 1 && channels[0] == "") + { + channels = new string[] { }; + } + if (message.Count >= 4) + { + if (message[message.Count - 2] is string[]) + { + channelGroups = message[message.Count - 2] as string[]; + } + else if (message[message.Count - 2].ToString() != "") + { + channelGroups = message[message.Count - 2].ToString().Split(','); + } + } + + long timetoken = GetTimetokenFromMultiplexResult(message); + int region = GetRegionFromMultiplexResult(message); + Task.Factory.StartNew(() => + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} MultiplexInternalCallback timetoken = {1}; region = {2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), timetoken, region), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + MultiChannelSubscribeRequest(type, channels, channelGroups, timetoken, region, false, null, this.customQueryParam); + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Lost Channel Name for resubscribe", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + } + + private bool ReconnectNetworkIfOverrideTcpKeepAlive(PNOperationType type, string[] channels, string[] channelGroups, object timetoken, int region, bool networkAvailable) + { + if (OverrideTcpKeepAlive) + { + ReconnectState netState = new ReconnectState(); + netState.Channels = channels; + netState.ChannelGroups = channelGroups; + netState.ResponseType = type; + netState.Timetoken = timetoken; + netState.Region = region; + if (!config.ContainsKey(PubnubInstance.InstanceId)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, InstanceId Not Available. So no reconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), PNLogVerbosity.BODY); + } + + if (SubscribeDisconnected[PubnubInstance.InstanceId]) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Subscribe is still Disconnected. So no reconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + } + else if (config[PubnubInstance.InstanceId].ReconnectionPolicy != PNReconnectionPolicy.NONE) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Subscribe - No internet connection for channel={1} and channelgroup={2}; networkAvailable={3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), string.Join(",", channels.OrderBy(x => x).ToArray()), channelGroups != null ? string.Join(",", channelGroups) : "", networkAvailable), config[PubnubInstance.InstanceId].LogVerbosity); + TerminateReconnectTimer(); + ReconnectNetwork(netState); + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, reconnection policy is DISABLED, please handle reconnection manually.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + if (!networkAvailable) + { + PNStatusCategory errorCategory = PNStatusCategory.PNNetworkIssuesCategory; + PNStatus status = new StatusBuilder(config[PubnubInstance.InstanceId], jsonLibrary).CreateStatusResponse(type, errorCategory, null, (int)HttpStatusCode.NotFound, new PNException("SDK Network related error")); + if (channels != null && channels.Length > 0) + { + status.AffectedChannels.AddRange(channels); + } + if (channelGroups != null && channelGroups.Length > 0) + { + status.AffectedChannels.AddRange(channelGroups); + } + Announce(status); + + } + } + return true; + } + else + { + return false; + } + } + + private void ReconnectNetwork(ReconnectState netState) + { + if (netState != null && ((netState.Channels != null && netState.Channels.Length > 0) || (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0))) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager ReconnectNetwork interval = {1} sec", DateTime.Now.ToString(CultureInfo.InvariantCulture), PubnubNetworkTcpCheckIntervalInSeconds), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + + System.Threading.Timer timer; + + if (netState.Channels != null && netState.Channels.Length > 0) + { + string reconnectChannelTimerKey = string.Join(",", netState.Channels.OrderBy(x => x).ToArray()); + + if (!ChannelReconnectTimer[PubnubInstance.InstanceId].ContainsKey(reconnectChannelTimerKey)) + { + timer = new Timer(new TimerCallback(ReconnectNetworkCallback), netState, 0, + (-1 == PubnubNetworkTcpCheckIntervalInSeconds) ? Timeout.Infinite : PubnubNetworkTcpCheckIntervalInSeconds * 1000); + ChannelReconnectTimer[PubnubInstance.InstanceId].AddOrUpdate(reconnectChannelTimerKey, timer, (key, oldState) => timer); + } + } + else if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + string reconnectChannelGroupTimerKey = string.Join(",", netState.ChannelGroups.OrderBy(x => x).ToArray()); + + if (!ChannelGroupReconnectTimer[PubnubInstance.InstanceId].ContainsKey(reconnectChannelGroupTimerKey)) + { + timer = new Timer(new TimerCallback(ReconnectNetworkCallback), netState, 0, + (-1 == PubnubNetworkTcpCheckIntervalInSeconds) ? Timeout.Infinite : PubnubNetworkTcpCheckIntervalInSeconds * 1000); + ChannelGroupReconnectTimer[PubnubInstance.InstanceId].AddOrUpdate(reconnectChannelGroupTimerKey, timer, (key, oldState) => timer); + } + } + } + } + + internal bool Reconnect(bool resetSubscribeTimetoken) + { + if (!SubscribeDisconnected[PubnubInstance.InstanceId]) //Check if disconnect is done before + { + return false; + } + + string[] channels = GetCurrentSubscriberChannels(); + string[] chananelGroups = GetCurrentSubscriberChannelGroups(); + + if ((channels != null && channels.Length > 0) || (chananelGroups != null && chananelGroups.Length > 0)) + { + string channel = (channels != null && channels.Length > 0) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ","; + string channelGroup = (chananelGroups != null && chananelGroups.Length > 0) ? string.Join(",", chananelGroups.OrderBy(x => x).ToArray()) : ""; + + bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, PNOperationType.PNSubscribeOperation, null, channels, chananelGroups); + if (!networkConnection) + { + //Recheck for false alert with 1 sec delay +#if !NET35 && !NET40 + Task.Delay(1000).Wait(); +#else + Thread.Sleep(1000); +#endif + + networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, PNOperationType.PNSubscribeOperation, null, channels, chananelGroups); + } + if (networkConnection) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Network available for SubscribeManager Manual Reconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + if (!string.IsNullOrEmpty(channel) && ChannelInternetStatus[PubnubInstance.InstanceId].ContainsKey(channel)) + { + ChannelInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(channel, networkConnection, (key, oldValue) => networkConnection); + } + if (!string.IsNullOrEmpty(channelGroup) && ChannelGroupInternetStatus[PubnubInstance.InstanceId].ContainsKey(channelGroup)) + { + ChannelGroupInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(channelGroup, networkConnection, (key, oldValue) => networkConnection); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, No network for SubscribeManager Manual Reconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + + PNStatusCategory errorCategory = PNStatusCategory.PNNetworkIssuesCategory; + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(PNOperationType.PNSubscribeOperation, errorCategory, null, (int)HttpStatusCode.NotFound, new PNException("SDK Network related error")); + if (channels != null && channels.Length > 0) + { + status.AffectedChannels.AddRange(channels); + } + if (chananelGroups != null && chananelGroups.Length > 0) + { + status.AffectedChannels.AddRange(chananelGroups); + } + Announce(status); + + return false; + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, No channels/channelgroups for SubscribeManager Manual Reconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + return false; + } + + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager Manual Reconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + SubscribeDisconnected[PubnubInstance.InstanceId] = false; + + Task.Factory.StartNew(() => + { + if (resetSubscribeTimetoken) + { + LastSubscribeTimetoken[PubnubInstance.InstanceId] = 0; + LastSubscribeRegion[PubnubInstance.InstanceId] = 0; + } + MultiChannelSubscribeRequest(PNOperationType.PNSubscribeOperation, GetCurrentSubscriberChannels(), GetCurrentSubscriberChannelGroups(), LastSubscribeTimetoken[PubnubInstance.InstanceId], LastSubscribeRegion[PubnubInstance.InstanceId], false, null, this.customQueryParam); + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + + return true; + } + + internal bool Disconnect() + { + if (SubscribeDisconnected[PubnubInstance.InstanceId]) + { + return false; + } + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager Manual Disconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + SubscribeDisconnected[PubnubInstance.InstanceId] = true; + TerminateCurrentSubscriberRequest(); + PubnubCoreBase2.TerminatePresenceHeartbeatTimer(); + TerminateReconnectTimer(); + + return true; + } + + internal void StartSubscribeHeartbeatCheckCallback(object state) + { + try + { + if (SubscribeDisconnected[PubnubInstance.InstanceId]) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - SubscribeDisconnected. No heartbeat check.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + return; + } + if (!config.ContainsKey(PubnubInstance.InstanceId)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, InstanceId Not Available. So No heartbeat check.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + return; + } + + string[] channels = GetCurrentSubscriberChannels(); + string[] chananelGroups = GetCurrentSubscriberChannelGroups(); + + if ((channels != null && channels.Length > 0) || (chananelGroups != null && chananelGroups.Length > 0)) + { + bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, PNOperationType.PNSubscribeOperation, null, channels, chananelGroups); + if (networkConnection && PubnubInstance != null && SubscribeRequestTracker.ContainsKey(PubnubInstance.InstanceId)) + { + DateTime lastSubscribeRequestTime = SubscribeRequestTracker[PubnubInstance.InstanceId]; + if ((DateTime.Now - lastSubscribeRequestTime).TotalSeconds < config[PubnubInstance.InstanceId].SubscribeTimeout) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - ok. expected subscribe within threshold limit of SubscribeTimeout. No action needed", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + } + else if ((DateTime.Now - lastSubscribeRequestTime).TotalSeconds > 2 * (config[PubnubInstance.InstanceId].SubscribeTimeout - config[PubnubInstance.InstanceId].SubscribeTimeout/2)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - **No auto subscribe within threshold limit of SubscribeTimeout**. Calling MultiChannelSubscribeRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + Task.Factory.StartNew(() => + { + TerminateCurrentSubscriberRequest(); + MultiChannelSubscribeRequest(PNOperationType.PNSubscribeOperation, channels, chananelGroups, LastSubscribeTimetoken[PubnubInstance.InstanceId], LastSubscribeRegion[PubnubInstance.InstanceId], false, null, this.customQueryParam); + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - **No auto subscribe within threshold limit of SubscribeTimeout**. Calling TerminateCurrentSubscriberRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + Task.Factory.StartNew(() => + { + TerminateCurrentSubscriberRequest(); + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - StartSubscribeHeartbeatCheckCallback - No network or no pubnub instance mapping", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + if (PubnubInstance != null && !networkConnection) + { + PNStatus status = new StatusBuilder(config[PubnubInstance.InstanceId], jsonLibrary).CreateStatusResponse(PNOperationType.PNSubscribeOperation, PNStatusCategory.PNNetworkIssuesCategory, null, (int)System.Net.HttpStatusCode.NotFound, new PNException("Internet connection problem during subscribe heartbeat.")); + if (channels != null && channels.Length > 0) + { + status.AffectedChannels.AddRange(channels.ToList()); + } + if (chananelGroups != null && chananelGroups.Length > 0) + { + status.AffectedChannelGroups.AddRange(chananelGroups.ToList()); + } + Announce(status); + + Task.Factory.StartNew(() => + { + TerminateCurrentSubscriberRequest(); + MultiChannelSubscribeRequest(PNOperationType.PNSubscribeOperation, GetCurrentSubscriberChannels(), GetCurrentSubscriberChannelGroups(), LastSubscribeTimetoken[PubnubInstance.InstanceId], LastSubscribeRegion[PubnubInstance.InstanceId], false, null, this.customQueryParam); + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); + } + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - StartSubscribeHeartbeatCheckCallback - No channels/cgs avaialable", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config[PubnubInstance.InstanceId].LogVerbosity); + try + { + SubscribeHeartbeatCheckTimer.Change(Timeout.Infinite, Timeout.Infinite); + TerminateCurrentSubscriberRequest(); + } + catch { /* ignore */ } + } + } + catch (Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager - StartSubscribeHeartbeatCheckCallback - EXCEPTION: {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture),ex), config[PubnubInstance.InstanceId].LogVerbosity); + } + } + + + protected void ReconnectNetworkCallback(System.Object reconnectState) + { + string channel = ""; + string channelGroup = ""; + + ReconnectState netState = reconnectState as ReconnectState; + try + { + string subscribedChannels = (MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.OrderBy(x => x).Aggregate((x, y) => x + "," + y) : ""; + string subscribedChannelGroups = (MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.OrderBy(x => x).Aggregate((x, y) => x + "," + y) : ""; + List channelRequestKeyList = ChannelRequest[PubnubInstance.InstanceId].Keys.ToList(); + for(int keyIndex= 0; keyIndex < channelRequestKeyList.Count; keyIndex++) + { + string keyChannel = channelRequestKeyList[keyIndex]; + if (keyChannel != subscribedChannels) + { + if (ChannelRequest[PubnubInstance.InstanceId].ContainsKey(keyChannel)) + { + HttpWebRequest keyChannelRequest; + ChannelRequest[PubnubInstance.InstanceId].TryGetValue(keyChannel, out keyChannelRequest); + if (keyChannelRequest != null) + { + try + { + keyChannelRequest.Abort(); + } + catch { /* ignore */ } + ChannelRequest[PubnubInstance.InstanceId].TryUpdate(keyChannel, null, keyChannelRequest); + } + HttpWebRequest tempValue; + ChannelRequest[PubnubInstance.InstanceId].TryRemove(keyChannel, out tempValue); + } + } + } + + + if (netState != null && ((netState.Channels != null && netState.Channels.Length > 0) || (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0))) + { + if (netState.Channels == null) + { + netState.Channels = new string[] { }; + } + + if (netState.ChannelGroups == null) + { + netState.ChannelGroups = new string[] { }; + } + + bool channelInternetFlag; + bool channelGroupInternetFlag; + if (netState.Channels != null && netState.Channels.Length > 0) + { + channel = (netState.Channels.Length > 0) ? string.Join(",", netState.Channels.OrderBy(x=>x).ToArray()) : ","; + channelGroup = (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) ? string.Join(",", netState.ChannelGroups.OrderBy(x => x).ToArray()) : ""; + + if (channel == subscribedChannels && ChannelInternetStatus[PubnubInstance.InstanceId].ContainsKey(channel) + && (netState.ResponseType == PNOperationType.PNSubscribeOperation || netState.ResponseType == PNOperationType.Presence)) + { + bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, netState.ResponseType, netState.PubnubCallback, netState.Channels, netState.ChannelGroups); + if (networkConnection) { + //Re-try to avoid false alert + networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, netState.ResponseType, netState.PubnubCallback, netState.Channels, netState.ChannelGroups); + } + + if (ChannelInternetStatus[PubnubInstance.InstanceId].TryGetValue(channel, out channelInternetFlag) && channelInternetFlag) + { + //do nothing + } + else + { + ChannelInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(channel, networkConnection, (key, oldValue) => networkConnection); + if (!string.IsNullOrEmpty(channelGroup) && channelGroup.Length > 0) + { + ChannelGroupInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(channelGroup, networkConnection, (key, oldValue) => networkConnection); + } + + ConnectionErrors++; + UpdatePubnubNetworkTcpCheckIntervalInSeconds(); + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, channel={1} {2} reconnectNetworkCallback. Retry", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel, netState.ResponseType), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + + if (netState.Channels != null && netState.Channels.Length > 0) + { + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(netState.ResponseType, PNStatusCategory.PNReconnectedCategory, null, (int)System.Net.HttpStatusCode.NotFound, new PNException("Internet connection problem. Retrying connection")); + if (netState.Channels != null && netState.Channels.Length > 0) + { + status.AffectedChannels.AddRange(netState.Channels.ToList()); + } + if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + status.AffectedChannelGroups.AddRange(netState.ChannelGroups.ToList()); + } + Announce(status); + } + + } + } + + if (ChannelInternetStatus[PubnubInstance.InstanceId].ContainsKey(channel) && ChannelInternetStatus[PubnubInstance.InstanceId].TryGetValue(channel, out channelInternetFlag) && channelInternetFlag) + { + if (ChannelReconnectTimer[PubnubInstance.InstanceId].ContainsKey(channel)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, {1} {2} terminating ch reconnectimer", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel, netState.ResponseType), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + TerminateReconnectTimer(); + } + + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(netState.ResponseType, PNStatusCategory.PNReconnectedCategory, null, (int)System.Net.HttpStatusCode.OK, null); + if (netState.Channels != null && netState.Channels.Length > 0) + { + status.AffectedChannels.AddRange(netState.Channels.ToList()); + } + if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + status.AffectedChannelGroups.AddRange(netState.ChannelGroups.ToList()); + } + Announce(status); + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, channel={1} {2} reconnectNetworkCallback. Internet Available : {3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel, netState.ResponseType, channelInternetFlag.ToString()), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + switch (netState.ResponseType) + { + case PNOperationType.PNSubscribeOperation: + case PNOperationType.Presence: + MultiChannelSubscribeRequest(netState.ResponseType, netState.Channels, netState.ChannelGroups, netState.Timetoken, netState.Region, true, null, this.customQueryParam); + break; + default: + break; + } + } + } + else if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + channelGroup = string.Join(",", netState.ChannelGroups.OrderBy(x => x).ToArray()); + channel = (netState.Channels != null && netState.Channels.Length > 0) ? string.Join(",", netState.Channels.OrderBy(x => x).ToArray()) : ","; + + if (subscribedChannelGroups == channelGroup && channelGroup != "" && ChannelGroupInternetStatus[PubnubInstance.InstanceId].ContainsKey(channelGroup) + && (netState.ResponseType == PNOperationType.PNSubscribeOperation || netState.ResponseType == PNOperationType.Presence)) + { + bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, netState.ResponseType, netState.PubnubCallback, netState.Channels, netState.ChannelGroups); + if (networkConnection) + { + //Re-try to avoid false alert + networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, netState.ResponseType, netState.PubnubCallback, netState.Channels, netState.ChannelGroups); + } + + if (ChannelGroupInternetStatus[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupInternetFlag) && channelGroupInternetFlag) + { + //do nothing + } + else + { + ChannelGroupInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(channelGroup, networkConnection, (key, oldValue) => networkConnection); + if (!string.IsNullOrEmpty(channel) && channel.Length > 0) + { + ChannelInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(channel, networkConnection, (key, oldValue) => networkConnection); + } + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, channelgroup={1} {2} reconnectNetworkCallback. Retrying", DateTime.Now.ToString(CultureInfo.InvariantCulture), channelGroup, netState.ResponseType), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + + if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(netState.ResponseType, PNStatusCategory.PNReconnectedCategory, null, (int)System.Net.HttpStatusCode.NotFound, new PNException("Internet connection problem. Retrying connection")); + if (netState.Channels != null && netState.Channels.Length > 0) + { + status.AffectedChannels.AddRange(netState.Channels.ToList()); + } + if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + status.AffectedChannelGroups.AddRange(netState.ChannelGroups.ToList()); + } + Announce(status); + } + } + } + + if (ChannelGroupInternetStatus[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupInternetFlag) && channelGroupInternetFlag) + { + if (ChannelGroupReconnectTimer[PubnubInstance.InstanceId].ContainsKey(channelGroup)) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, {1} {2} terminating cg reconnectimer", DateTime.Now.ToString(CultureInfo.InvariantCulture), channelGroup, netState.ResponseType), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + TerminateReconnectTimer(); + } + + //Send one ReConnectedCategory message. If Channels NOT available then use this + if (netState.Channels.Length == 0 && netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(netState.ResponseType, PNStatusCategory.PNReconnectedCategory, null, (int)System.Net.HttpStatusCode.OK, null); + if (netState.Channels != null && netState.Channels.Length > 0) + { + status.AffectedChannels.AddRange(netState.Channels.ToList()); + } + if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + status.AffectedChannelGroups.AddRange(netState.ChannelGroups.ToList()); + } + Announce(status); + } + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, channelgroup={1} {2} reconnectNetworkCallback. Internet Available", DateTime.Now.ToString(CultureInfo.InvariantCulture), channelGroup, netState.ResponseType), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + switch (netState.ResponseType) + { + case PNOperationType.PNSubscribeOperation: + case PNOperationType.Presence: + MultiChannelSubscribeRequest(netState.ResponseType, netState.Channels, netState.ChannelGroups, netState.Timetoken, netState.Region, true, null, this.customQueryParam); + break; + default: + break; + } + } + } + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Unknown request state in reconnectNetworkCallback", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + } + catch (Exception ex) + { + if (netState != null) + { + PNStatusCategory errorCategory = PNStatusCategoryHelper.GetPNStatusCategory(ex); + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(netState.ResponseType, errorCategory, null, (int)HttpStatusCode.NotFound, new PNException(ex)); + if (netState.Channels != null && netState.Channels.Length > 0) + { + status.AffectedChannels.AddRange(netState.Channels.ToList()); + } + if (netState.ChannelGroups != null && netState.ChannelGroups.Length > 0) + { + status.AffectedChannels.AddRange(netState.ChannelGroups.ToList()); + } + Announce(status); + } + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} method:reconnectNetworkCallback \n Exception Details={1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); + } + } + + private void RegisterPresenceHeartbeatTimer(string[] channels, string[] channelGroups) + { + if (PresenceHeartbeatTimer != null) + { + try + { + PresenceHeartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite); + PresenceHeartbeatTimer.Dispose(); + PresenceHeartbeatTimer = null; + } + catch { /* ignore */ } + } + if ((channels != null && channels.Length > 0 && channels.Where(s => s != null && s.Contains("-pnpres") == false).Any()) + || (channelGroups != null && channelGroups.Length > 0 && channelGroups.Where(s => s != null && s.Contains("-pnpres") == false).Any())) + { + RequestState presenceHeartbeatState = new RequestState(); + presenceHeartbeatState.Channels = channels; + presenceHeartbeatState.ChannelGroups = channelGroups; + presenceHeartbeatState.ResponseType = PNOperationType.PNHeartbeatOperation; + presenceHeartbeatState.Request = null; + presenceHeartbeatState.Response = null; + + if (config.ContainsKey(PubnubInstance.InstanceId) && config[PubnubInstance.InstanceId].PresenceInterval > 0) + { + PresenceHeartbeatTimer = new Timer(OnPresenceHeartbeatIntervalTimeout, presenceHeartbeatState, config[PubnubInstance.InstanceId].PresenceInterval * 1000, config[PubnubInstance.InstanceId].PresenceInterval * 1000); + } + } + } + +#pragma warning disable + void OnPresenceHeartbeatIntervalTimeout(System.Object presenceHeartbeatState) +#pragma warning restore + { + //Make presence heartbeat call + RequestState currentState = presenceHeartbeatState as RequestState; + if (currentState != null) + { + string[] subscriberChannels = (currentState.Channels != null) ? currentState.Channels.Where(s => s.Contains("-pnpres") == false).ToArray() : null; + string[] subscriberChannelGroups = (currentState.ChannelGroups != null) ? currentState.ChannelGroups.Where(s => s.Contains("-pnpres") == false).ToArray() : null; + + bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, currentState.ResponseType, currentState.PubnubCallback, currentState.Channels, currentState.ChannelGroups); + if (networkConnection) + { + if ((subscriberChannels != null && subscriberChannels.Length > 0) || (subscriberChannelGroups != null && subscriberChannelGroups.Length > 0)) + { + string channelsJsonState = BuildJsonUserState(subscriberChannels, subscriberChannelGroups, false); + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); + + Uri request = urlBuilder.BuildPresenceHeartbeatRequest("GET", "", subscriberChannels, subscriberChannelGroups, channelsJsonState); + + RequestState requestState = new RequestState(); + requestState.Channels = currentState.Channels; + requestState.ChannelGroups = currentState.ChannelGroups; + requestState.ResponseType = PNOperationType.PNHeartbeatOperation; + requestState.PubnubCallback = null; + requestState.Reconnect = false; + requestState.Response = null; + requestState.TimeQueued = DateTime.Now; + + UrlProcessRequest(request, requestState, false).ContinueWith(r => + { + string json = r.Result.Item1; + if (!string.IsNullOrEmpty(json)) + { + List result = ProcessJsonResponse(requestState, json); + ProcessResponseCallbacks(result, requestState); + } + }, TaskContinuationOptions.ExecuteSynchronously).Wait(); + } + } + else + { + if (PubnubInstance != null && !networkConnection) + { + PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(PNOperationType.PNSubscribeOperation, PNStatusCategory.PNNetworkIssuesCategory, null, (int)System.Net.HttpStatusCode.NotFound, new PNException("Internet connection problem during presence heartbeat.")); + if (subscriberChannels != null && subscriberChannels.Length > 0) + { + status.AffectedChannels.AddRange(subscriberChannels.ToList()); + } + if (subscriberChannelGroups != null && subscriberChannelGroups.Length > 0) + { + status.AffectedChannelGroups.AddRange(subscriberChannelGroups.ToList()); + } + Announce(status); + } + + } + } + + } + + internal void CurrentPubnubInstance(Pubnub instance) + { + PubnubInstance = instance; + } + + #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 03d06ae1b..3ccf66be1 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using System.Net; using System.Globalization; -using PNEventEngine; namespace PubnubApi.EndPoint { @@ -26,7 +25,6 @@ public class SubscribeOperation : PubnubCoreBase private bool presenceSubscribeEnabled; private SubscribeManager manager; private Dictionary queryParam; - private EventEngine pnEventEngine; public SubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) { @@ -36,12 +34,6 @@ public SubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary js pubnubLog = log; pubnubTelemetryMgr = telemetryManager; pubnubTokenMgr = tokenManager; - - //var effectDispatcher = new EffectDispatcher(); - //var eventEmitter = new EventEmitter(); - //var handshakeEffect = new HandshakeEffectHandler(null, eventEmitter); - //var receivingEffect = new ReceivingEffectHandler(null, eventEmitter); - //var reconnectionEffect = new ReconnectingEffectHandler(null, eventEmitter); } public SubscribeOperation Channels(string[] channels) @@ -150,7 +142,6 @@ private void Subscribe(string[] channels, string[] channelGroups, Dictionary { - //pnEventEngine = new EventEngine(null, null); manager = new SubscribeManager(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); manager.CurrentPubnubInstance(PubnubInstance); manager.MultiChannelSubscribeInit(PNOperationType.PNSubscribeOperation, channels, channelGroups, initialSubscribeUrlParams, externalQueryParam); diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs new file mode 100644 index 000000000..fd43a3c63 --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using PubnubApi.Interface; +using System.Threading.Tasks; +using System.Net; +using System.Globalization; +using PubnubApi.PubnubEventEngine; + +namespace PubnubApi.EndPoint +{ + public class SubscribeOperation2 : PubnubCoreBase2 + { + 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 EventEngine pnEventEngine; + + public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) + { + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + pubnubTokenMgr = tokenManager; + + var effectDispatcher = new EffectDispatcher(); + var eventEmitter = new EventEmitter(); + var handshakeEffect = new HandshakeEffectHandler(eventEmitter); + handshakeEffect.LogCallback = LogCallback; + handshakeEffect.HandshakeRequested += HandshakeEffect_HandshakeRequested; + + var receivingEffect = new ReceivingEffectHandler(eventEmitter); + receivingEffect.LogCallback = LogCallback; + receivingEffect.ReceiveRequested += ReceivingEffect_ReceiveRequested; + + var reconnectionEffect = new ReconnectingEffectHandler(eventEmitter); + + effectDispatcher.Register(EffectType.SendHandshakeRequest, handshakeEffect); + effectDispatcher.Register(EffectType.ReceiveEventRequest, receivingEffect); + effectDispatcher.Register(EffectType.ReconnectionAttempt, reconnectionEffect); + + pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); + + var initState = pnEventEngine.CreateState(StateType.Unsubscribed) + .OnEntry(() => { System.Diagnostics.Debug.WriteLine("Unsubscribed: OnEntry()"); return true; }) + .OnExit(() => { System.Diagnostics.Debug.WriteLine("Unsubscribed: OnExit()"); return true; }) + .On(EventType.SubscriptionChange, StateType.Handshaking); + + pnEventEngine.CreateState(StateType.Handshaking) + .OnEntry(() => { System.Diagnostics.Debug.WriteLine("Handshaking: OnEntry()"); return true; }) + .OnExit(() => { System.Diagnostics.Debug.WriteLine("Handshaking: OnExit()"); return true; }) + .On(EventType.SubscriptionChange, StateType.Handshaking) + .On(EventType.HandshakeSuccess, StateType.Receiving) + .On(EventType.HandshakeFailed, StateType.Reconnecting) + .Effect(EffectType.SendHandshakeRequest); + + pnEventEngine.CreateState(StateType.Receiving) + .OnEntry(() => { System.Diagnostics.Debug.WriteLine("Receiving: OnEntry()"); return true; }) + .OnExit(() => { System.Diagnostics.Debug.WriteLine("Receiving: OnExit()"); return true; }) + .On(EventType.SubscriptionChange, StateType.Handshaking) + .On(EventType.ReceiveSuccess, StateType.Receiving) + .On(EventType.ReceiveFailed, StateType.Reconnecting) + .Effect(EffectType.ReceiveEventRequest); + + pnEventEngine.CreateState(StateType.HandshakingFailed) + .OnEntry(() => { System.Diagnostics.Debug.WriteLine("HandshakingFailed: OnEntry()"); return true; }) + .OnExit(() => { System.Diagnostics.Debug.WriteLine("HandshakingFailed: OnExit()"); return true; }) + .On(EventType.SubscriptionChange, StateType.Handshaking) + .On(EventType.HandshakeSuccess, StateType.Receiving) + .On(EventType.HandshakeFailed, StateType.Reconnecting); + + pnEventEngine.CreateState(StateType.Reconnecting) + .OnEntry(() => { System.Diagnostics.Debug.WriteLine("Reconnecting: OnEntry()"); return true; }) + .OnExit(() => { System.Diagnostics.Debug.WriteLine("Reconnecting: OnExit()"); return true; }) + .On(EventType.SubscriptionChange, StateType.Handshaking) + .On(EventType.HandshakeSuccess, StateType.Receiving) + .On(EventType.HandshakeFailed, StateType.Reconnecting); + + pnEventEngine.InitialState(initState); + + } + + private void ReceivingEffect_ReceiveRequested(object sender, ReceiveRequestEventArgs e) + { + manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); + manager.CurrentPubnubInstance(PubnubInstance); + + string jsonResp = manager.HandshakeRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; + + e.ReceiveResponseCallback?.Invoke(jsonResp); + } + + private void HandshakeEffect_HandshakeRequested(object sender, HandshakeRequestEventArgs e) + { + manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); + manager.CurrentPubnubInstance(PubnubInstance); + + string jsonResp = manager.HandshakeRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; + + e.HandshakeResponseCallback?.Invoke(jsonResp); + } + + private void LogCallback(string log) + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), log), config.LogVerbosity); + } + + public SubscribeOperation2 Channels(string[] channels) + { + if (channels != null && channels.Length > 0 && !string.IsNullOrEmpty(channels[0])) + { + this.subscribeChannelNames.AddRange(channels); + } + return this; + } + + public SubscribeOperation2 ChannelGroups(string[] channelGroups) + { + if (channelGroups != null && channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0])) + { + this.subscribeChannelGroupNames.AddRange(channelGroups); + } + return this; + } + + public SubscribeOperation2 WithTimetoken(long timetoken) + { + this.subscribeTimetoken = timetoken; + return this; + } + + public SubscribeOperation2 WithPresence() + { + this.presenceSubscribeEnabled = true; + return this; + } + + public SubscribeOperation2 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) + { + if ((channels == null || channels.Length == 0) && (channelGroups == null || channelGroups.Length == 0)) + { + throw new ArgumentException("Either Channel Or Channel Group or Both should be provided."); + } + + string channel = (channels != null) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ""; + string channelGroup = (channelGroups != null) ? string.Join(",", channelGroups.OrderBy(x => x).ToArray()) : ""; + + PNPlatform.Print(config, pubnubLog); + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, requested subscribe for channel(s)={1} and channel group(s)={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel, channelGroup), config.LogVerbosity); + + Dictionary initialSubscribeUrlParams = new Dictionary(); + if (this.subscribeTimetoken >= 0) + { + initialSubscribeUrlParams.Add("tt", this.subscribeTimetoken.ToString(CultureInfo.InvariantCulture)); + } + if (!string.IsNullOrEmpty(config.FilterExpression) && config.FilterExpression.Trim().Length > 0) + { + initialSubscribeUrlParams.Add("filter-expr", UriUtil.EncodeUriComponent(config.FilterExpression, PNOperationType.PNSubscribeOperation, false, false, false)); + } + +#if NETFX_CORE || WINDOWS_UWP || UAP || NETSTANDARD10 || NETSTANDARD11 || NETSTANDARD12 + Task.Factory.StartNew(() => + { + pnEventEngine.Subscribe(channels.ToList(), channelGroups.ToList()); + //manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); + //manager.CurrentPubnubInstance(PubnubInstance); + //manager.MultiChannelSubscribeInit(PNOperationType.PNSubscribeOperation, channels, channelGroups, initialSubscribeUrlParams, externalQueryParam); + }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); +#else + new Thread(() => + { + pnEventEngine.Subscribe(channels.ToList(), channelGroups.ToList()); + //manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); + //manager.CurrentPubnubInstance(PubnubInstance); + //manager.MultiChannelSubscribeInit(PNOperationType.PNSubscribeOperation, channels, channelGroups, initialSubscribeUrlParams, externalQueryParam); + }) + { IsBackground = true }.Start(); +#endif + } + + internal bool Retry(bool reconnect) + { + if (manager == null) + { + return false; + } + + if (reconnect) + { + return manager.Reconnect(false); + } + else + { + return manager.Disconnect(); + } + } + + internal bool Retry(bool reconnect, bool resetSubscribeTimetoken) + { + if (manager == null) + { + return false; + } + + if (reconnect) + { + return manager.Reconnect(resetSubscribeTimetoken); + } + else + { + return manager.Disconnect(); + } + } + + 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/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs new file mode 100644 index 000000000..d2a2e0472 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace PubnubApi.PubnubEventEngine +{ + public class EffectDispatcher + { + public Dictionary effectActionMap; + public EffectDispatcher() + { + effectActionMap = new Dictionary(); + } + + public async void dispatch(EffectType effect, ExtendedState stateContext) + { + IEffectHandler? handler; + if (effectActionMap.TryGetValue(effect, out handler)) { + if (handler != null) + { + await Task.Factory.StartNew(()=> handler.Start(stateContext)).ConfigureAwait(false);; + } + } + } + + public void Register(EffectType type, IEffectHandler handler) + { + effectActionMap.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..d7071a899 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/EventEmitter.cs @@ -0,0 +1,23 @@ +using System; + +namespace PubnubApi.PubnubEventEngine +{ + public class EventEmitter + { + public Action? handler; + + public void RegisterHandler(Action eventHandler) + { + this.handler = eventHandler; + } + + public void emit(Event e) + { + if (handler == null) + { + throw new MissingMemberException("eventHandler is missing"); + } + this.handler(e); + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs new file mode 100644 index 000000000..54dbeaefd --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PubnubApi.PubnubEventEngine +{ + public class Event + { + public EventType Type { get; set; } + public EventPayload EventPayload { get; set; } + + public Event() + { + EventPayload = new EventPayload(); + } + } + + 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 enum EventType + { + SubscriptionChange, + HandshakeSuccess, + ReceiveSuccess, + HandshakeFailed, + ReceiveFailed, + ReconnectionFailed + } + + 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 AttemptedReconnection { get; set; } + + public ExtendedState() + { + Channels = new List(); + ChannelGroups = new List(); + Timetoken = 0; + Region = 0; + AttemptedReconnection = 0; + } + + } + + public class EventEngine + { + public ExtendedState Context; + public State? CurrentState { get; set; } + public List States { get; set; } + + public EffectDispatcher Dispatcher; + + public EventEmitter Emitter; + + public EventEngine(EffectDispatcher dispatcher, EventEmitter emitter) + { + 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); + States.Add(newState); + return newState; + } + + public void Transition(Event e) + { + StateType nextStateType; + if (CurrentState != null && CurrentState.transitions.TryGetValue(e.Type, out nextStateType)) { + CurrentState.Exit(); + CurrentState = this.States.Find((s) => s.Type == nextStateType); + UpdateContext(e.EventPayload); + if (CurrentState != null) + { + CurrentState.Entry(); + UpdateContext(e.EventPayload); + if (CurrentState.Effects.Count > 0) { + foreach (var effect in CurrentState.Effects) { + System.Diagnostics.Debug.WriteLine("Found effect "+ effect); + Dispatcher.dispatch(effect, this.Context); + } + } + } + } + } + + public void Subscribe(List channels, List? channelGroups) + { + var evnt = new Event(); + evnt.Type = EventType.SubscriptionChange; + evnt.EventPayload.Channels = channels; + if (channelGroups != null) evnt.EventPayload.ChannelGroups = channelGroups; + this.Transition(evnt); + } + + private void UpdateContext(EventPayload eventData) + { + if (eventData.Channels != null) Context.Channels = eventData.Channels; + if (eventData.ChannelGroups != null) Context.ChannelGroups = eventData.ChannelGroups; + if (eventData.Timetoken != null) Context.Timetoken = eventData.Timetoken; + if (eventData.Region != null) Context.Region = eventData.Region; + } + + public void InitialState(State state) + { + this.CurrentState = state; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs new file mode 100644 index 000000000..ac1f8ef96 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs @@ -0,0 +1,90 @@ +using System; +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 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 HandshakeEffectHandler : IEffectHandler + { + EventEmitter emitter; + public Action LogCallback { get; set; } + + public event EventHandler? HandshakeRequested; + protected virtual void OnHandshakeRequested(HandshakeRequestEventArgs e) + { + EventHandler? handler = HandshakeRequested; + if (handler != null) + { + handler(this, e); + } + } + + public HandshakeEffectHandler(EventEmitter emitter) + { + this.emitter = emitter; + } + public async void Start(ExtendedState context) + { + await Task.Factory.StartNew(() => { }); + HandshakeRequestEventArgs args = new HandshakeRequestEventArgs(); + args.ExtendedState = context; + args.HandshakeResponseCallback = OnHandshakeEffectResponseReceived; + OnHandshakeRequested(args); + // TODO: Replace with Stateless Utility Methods + // TODO: Fetch Configuration from PubNub instance + } + + public void OnHandshakeEffectResponseReceived(string json) + { + var evnt = new Event(); + try { + LogCallback?.Invoke($"Handshake Json Response { json }"); + var handshakeResponse = JsonConvert.DeserializeObject(json); + if (handshakeResponse != null) + { + evnt.EventPayload.Timetoken = handshakeResponse.Timetoken?.Timestamp; + evnt.EventPayload.Region = handshakeResponse.Timetoken?.Region; + evnt.Type = EventType.HandshakeSuccess; + } + } catch (Exception ex) { + LogCallback?.Invoke($"HandshakeEffectHandler EXCEPTION - {ex}"); + evnt.Type = EventType.HandshakeFailed; + evnt.EventPayload.exception = ex; + } + emitter.emit(evnt); + } + public void Cancel() + { + //Console.WriteLine("Handshake can not be cancelled. Something is not right here!"); + //LoggingMethod.WriteToLog("HandshakeEffectHandler - Handshake can not be cancelled. Something is not right here!"); + LogCallback?.Invoke($"HandshakeEffectHandler - Handshake can not be cancelled. Something is not right here!"); + } + + } +} diff --git a/src/Api/PubnubApi/EventEngine/IEffectHandler.cs b/src/Api/PubnubApi/EventEngine/IEffectHandler.cs new file mode 100644 index 000000000..920ac3d86 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/IEffectHandler.cs @@ -0,0 +1,18 @@ +using System; + +namespace PubnubApi.PubnubEventEngine +{ + + public enum EffectType + { + SendHandshakeRequest, // oneshot + ReceiveEventRequest, // long running + ReconnectionAttempt + } + + public interface IEffectHandler + { + void Start(ExtendedState context); + void Cancel(); + } +} diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs new file mode 100644 index 000000000..944f9d16d --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -0,0 +1,158 @@ +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("c")] + public string Channel { get; set; } + + [JsonProperty("d")] + public object? Payload { 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 ReceiveRequestEventArgs : EventArgs + { + public ExtendedState ExtendedState { get; set; } + public Action ReceiveResponseCallback { get; set; } + } + public class ReceivingEffectHandler : IEffectHandler + { + EventEmitter emitter; + public Action LogCallback { get; set; } + public event EventHandler? ReceiveRequested; + protected virtual void OnReceiveRequested(ReceiveRequestEventArgs e) + { + EventHandler? handler = ReceiveRequested; + if (handler != null) + { + handler(this, e); + } + } + + CancellationTokenSource? cancellationTokenSource; + + public ReceivingEffectHandler(EventEmitter emitter) + { + this.emitter = emitter; + cancellationTokenSource = new CancellationTokenSource(); + } + + public async void Start(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); + + //var evnt = new Event(); + //// TODO: Replace with stateless Utility method... + //try { + // //var res = await httpClient.GetAsync($"https://ps.pndsn.com/v2/subscribe/sub-c-c9710928-1b7a-11e3-a0c8-02ee2ddab7fe/{String.Join(",", context.Channels.ToArray())}/0?uuid=cSharpTest&channel-group={String.Join(",", context.ChannelGroups.ToArray())}&tt={context.Timetoken}&tr={context.Region}", cancellationTokenSource.Token); + // //string jsonResp = await res.Content.ReadAsStringAsync(); + // ////LoggingMethod.WriteToLog(string.Format("ReceivingEffectHandler - {0}",jsonResp)); + // //var receivedResponse = JsonConvert.DeserializeObject(jsonResp); + // //evnt.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; + // //evnt.EventPayload.Region = receivedResponse.Timetoken.Region; + // //evnt.Type = EventType.ReceiveSuccess; + + // //if (receivedResponse.Messages != null) + // // Console.WriteLine($"Received Messages {JsonConvert.SerializeObject(receivedResponse.Messages)}"); //WIP: Define "DELIVERING" Effect. and transition + + //} catch (Exception ex) { + // evnt.Type = EventType.ReceiveFailed; + // //LoggingMethod.WriteToLog(string.Format("ReceivingEffectHandler EXCEPTION - {0}",ex)); + // evnt.EventPayload.exception = ex; + //} + //emitter.emit(evnt); + } + + public void OnReceivingEffectResponseReceived(string json) + { + var evnt = new Event(); + try { + var receivedResponse = JsonConvert.DeserializeObject(json); + if (receivedResponse != null) + { + evnt.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; + evnt.EventPayload.Region = receivedResponse.Timetoken.Region; + evnt.Type = EventType.ReceiveSuccess; + + if (receivedResponse.Messages != null && receivedResponse.Messages.Length > 0) + { + //WIP: Define "DELIVERING" Effect. and transition + for(int index = 0; index < receivedResponse.Messages.Length; index++) + { + LogCallback?.Invoke($"Received Message ({index+1} of {receivedResponse.Messages.Length}) : {JsonConvert.SerializeObject(receivedResponse.Messages[index])}"); + if (receivedResponse.Messages[index].Channel.IndexOf("-pnpres") > 0) + { + + var presenceData = JsonConvert.DeserializeObject(receivedResponse.Messages[index].Payload.ToString()); + LogCallback?.Invoke($"Presence Action : {presenceData?.Action}"); + LogCallback?.Invoke($"Presence Uuid : {presenceData?.Uuid}"); + LogCallback?.Invoke($"Presence Timestamp : {presenceData?.Timestamp}"); + LogCallback?.Invoke($"Presence Occupancy : {presenceData?.Occupancy}"); + } + else + { + LogCallback?.Invoke($"Message : {JsonConvert.SerializeObject(receivedResponse.Messages[index].Payload)}"); + } + } + //LogCallback?.Invoke($"Received Messages {JsonConvert.SerializeObject(receivedResponse.Messages)}"); + } + } + } catch (Exception ex) { + LogCallback?.Invoke($"ReceivingEffectHandler EXCEPTION - {ex}"); + + evnt.Type = EventType.ReceiveFailed; + evnt.EventPayload.exception = ex; + } + emitter.emit(evnt); + } + + public void Cancel() + { + //Console.WriteLine("Attempting cancellation"); + //LoggingMethod.WriteToLog("ReceivingEffectHandler - Attempting cancellation"); + if (cancellationTokenSource != null) + { + cancellationTokenSource.Cancel(); + } + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs new file mode 100644 index 000000000..43444820b --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs @@ -0,0 +1,30 @@ +using System; + +namespace PubnubApi.PubnubEventEngine +{ + public class ReconnectingEffectHandler : IEffectHandler + { + EventEmitter eventEmitter; + + //PNConfiguration pnConfig; + public ReconnectingEffectHandler(EventEmitter emitter) + { + this.eventEmitter = emitter; + //pnConfig = config; + } + + public void Start(ExtendedState context) // TODO: Implementation of retry getDelay() as per policy + { + var evnt = new Event(); + evnt.EventPayload.Timetoken = context.Timetoken; + evnt.EventPayload.Region = context.Region; + evnt.Type = EventType.ReceiveSuccess; + this.eventEmitter.emit(evnt); + } + + public void Cancel() + { + System.Diagnostics.Debug.WriteLine("Reconnecting Cancelled!!!"); + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/State.cs b/src/Api/PubnubApi/EventEngine/State.cs new file mode 100644 index 000000000..fc03d8823 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/State.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; + +namespace PubnubApi.PubnubEventEngine +{ + + public enum StateType { Unsubscribed, Handshaking, Receiving, HandshakingFailed, ReconnectingFailed, Reconnecting }; + + public class State + { + public StateType Type { get; set; } + public Dictionary transitions; + public List Effects { get; set; } + public Func Entry { get; set; } = () => { + return true; + }; + + public Func Exit { get; set; } = () => { + return true; + }; + + public State(StateType type) + { + this.Type = type; + this.transitions = new Dictionary(); + Effects = new List(); + } + + public State On(EventType e, StateType nextState) + { + transitions.Add(e, nextState); + return this; + } + + public State OnEntry(Func entry) + { + this.Entry = entry; + return this; + } + + public State OnExit(Func exit) + { + this.Exit = exit; + return this; + } + + public State Effect(EffectType effect) + { + this.Effects.Add(effect); + return this; + } + } +} diff --git a/src/Api/PubnubApi/PNConfiguration.cs b/src/Api/PubnubApi/PNConfiguration.cs index 1f63be270..c58cb17c3 100644 --- a/src/Api/PubnubApi/PNConfiguration.cs +++ b/src/Api/PubnubApi/PNConfiguration.cs @@ -136,6 +136,8 @@ public UserId UserId public bool SuppressLeaveEvents { get; set; } public bool UseRandomInitializationVector { get; set; } + + public bool EnableEventEngine { get; set; } public int FileMessagePublishRetryLimit { get; set; } [Obsolete("PNConfiguration(string uuid) is deprecated, please use PNConfiguration(UserId userId) instead.")] @@ -181,6 +183,7 @@ private void ConstructorInit(UserId currentUserId) UseRandomInitializationVector = true; FileMessagePublishRetryLimit = 5; _userId = currentUserId; + EnableEventEngine = true; } public PNConfiguration SetPresenceTimeoutWithCustomInterval(int timeout, int interval) { diff --git a/src/Api/PubnubApi/Pubnub.cs b/src/Api/PubnubApi/Pubnub.cs index 6d4914400..4d2be0e9a 100644 --- a/src/Api/PubnubApi/Pubnub.cs +++ b/src/Api/PubnubApi/Pubnub.cs @@ -37,6 +37,13 @@ public EndPoint.SubscribeOperation Subscribe() savedSubscribeOperation = subscribeOperation; return subscribeOperation; } + public EndPoint.SubscribeOperation2 Subscribe2() + { + EndPoint.SubscribeOperation2 subscribeOperation = new EndPoint.SubscribeOperation2(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); + subscribeOperation.CurrentPubnubInstance(this); + savedSubscribeOperation = subscribeOperation; + return subscribeOperation; + } public EndPoint.UnsubscribeOperation Unsubscribe() { diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index 07f9afe77..ba27402c8 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -2,6 +2,7 @@ net35;net40;net45;net461;net48 + 8.0 True pubnub.snk False @@ -131,7 +132,7 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. - + diff --git a/src/Api/PubnubApi/PubnubCoreBase2.cs b/src/Api/PubnubApi/PubnubCoreBase2.cs new file mode 100644 index 000000000..e3ba933a9 --- /dev/null +++ b/src/Api/PubnubApi/PubnubCoreBase2.cs @@ -0,0 +1,2502 @@ +//Build Date: April 13, 2017 +#region "Header" +#if (__MonoCS__) +#define TRACE +#endif +using System; +using System.Text; +using System.Net; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 +using System.Net.Http; +using System.Net.Http.Headers; +#endif +#endregion + +namespace PubnubApi +{ + public abstract class PubnubCoreBase2 + { + #region "Class variables" + private static bool enableResumeOnReconnect = true; + protected static bool OverrideTcpKeepAlive { get; set; } = true; + protected static System.Threading.Timer PresenceHeartbeatTimer { get; set; } + protected static bool PubnetSystemActive { get; set; } = true; + protected Collection PushRemoteImageDomainUri { get; set; } = new Collection(); + protected static int ConnectionErrors { get; set; } + #endregion + + private const int MINEXPONENTIALBACKOFF = 1; + private const int MAXEXPONENTIALBACKOFF = 32; + private const int INTERVAL = 3; + + private static IPubnubHttp pubnubHttp; + private static ConcurrentDictionary pubnubConfig { get; } = new ConcurrentDictionary(); + private static IJsonPluggableLibrary jsonLib; + private static IPubnubUnitTest unitTest; + private static ConcurrentDictionary pubnubLog { get; } = new ConcurrentDictionary(); + private static EndPoint.TelemetryManager pubnubTelemetryMgr; + protected static ConcurrentDictionary PubnubTokenMgrCollection { get; } = new ConcurrentDictionary(); + private static EndPoint.DuplicationManager pubnubSubscribeDuplicationManager { get; set; } +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + private static HttpClient httpClientSubscribe { get; set; } + private static HttpClient httpClientNonsubscribe { get; set; } + private static HttpClient httpClientNetworkStatus { get; set; } + private static PubnubHttpClientHandler pubnubHttpClientHandler { get; set; } +#endif + + private bool clientNetworkStatusInternetStatus = true; + protected static ConcurrentDictionary SubscribeDisconnected { get; set; } = new ConcurrentDictionary(); + + protected Pubnub PubnubInstance { get; set; } + + protected static ConcurrentDictionary UserIdChanged { get; set; } = new ConcurrentDictionary(); + + protected static ConcurrentDictionary CurrentUserId { get; set; } = new ConcurrentDictionary(); + + protected static ConcurrentDictionary LastSubscribeTimetoken { get; set; } = new ConcurrentDictionary(); + protected static ConcurrentDictionary LastSubscribeRegion { get; set; } = new ConcurrentDictionary(); + + protected static int PubnubNetworkTcpCheckIntervalInSeconds { get; set; } = 3; + private static int PubnubLocalHeartbeatCheckIntervalInSeconds { get; set; } = 30; + + protected static ConcurrentDictionary> SubscribeCallbackListenerList + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> MultiChannelSubscribe + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> MultiChannelGroupSubscribe + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> ChannelReconnectTimer + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> ChannelGroupReconnectTimer + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> ChannelRequest + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> ChannelInternetStatus + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary> ChannelGroupInternetStatus + { + get; + set; + } = new ConcurrentDictionary>(); + + protected static ConcurrentDictionary>> ChannelLocalUserState + { + get; + set; + } = new ConcurrentDictionary>>(); + + protected static ConcurrentDictionary>> ChannelUserState + { + get; + set; + } = new ConcurrentDictionary>>(); + + protected static ConcurrentDictionary>> ChannelGroupLocalUserState + { + get; + set; + } = new ConcurrentDictionary>>(); + + protected static ConcurrentDictionary>> ChannelGroupUserState + { + get; + set; + } = new ConcurrentDictionary>>(); + + protected static ConcurrentDictionary SubscribeRequestTracker + { + get; + set; + } = new ConcurrentDictionary(); + + protected PubnubCoreBase2(PNConfiguration pubnubConfiguation, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnitTest, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) + { + if (pubnubConfiguation == null) + { + throw new ArgumentException("PNConfiguration missing"); + } + + if (jsonPluggableLibrary == null) + { + InternalConstructor(pubnubConfiguation, new NewtonsoftJsonDotNet(pubnubConfiguation,log), pubnubUnitTest, log, telemetryManager, tokenManager, instance); + } + else + { + InternalConstructor(pubnubConfiguation, jsonPluggableLibrary, pubnubUnitTest, log, telemetryManager, tokenManager, instance); + } + } + + private void InternalConstructor(PNConfiguration pubnubConfiguation, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnitTest, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) + { + PubnubInstance = instance; + pubnubConfig.AddOrUpdate(instance.InstanceId, pubnubConfiguation, (k,o)=> pubnubConfiguation); + jsonLib = jsonPluggableLibrary; + unitTest = pubnubUnitTest; + pubnubLog.AddOrUpdate(instance.InstanceId, log, (k, o) => log); + PubnubTokenMgrCollection.AddOrUpdate(instance.InstanceId, tokenManager, (k,o)=> tokenManager); + pubnubTelemetryMgr = telemetryManager; + pubnubSubscribeDuplicationManager = new EndPoint.DuplicationManager(pubnubConfiguation, jsonPluggableLibrary, log); + + CurrentUserId.AddOrUpdate(instance.InstanceId, pubnubConfiguation.UserId, (k,o) => pubnubConfiguation.UserId); + +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + if (httpClientSubscribe == null) + { + if (pubnubConfiguation.Proxy != null) + { + HttpClientHandler httpClientHandler = new HttpClientHandler(); + if (httpClientHandler.SupportsProxy) + { + httpClientHandler.Proxy = pubnubConfiguation.Proxy; + httpClientHandler.UseProxy = true; + } + pubnubHttpClientHandler = new PubnubHttpClientHandler("PubnubHttpClientHandler", httpClientHandler, pubnubConfiguation, jsonLib, unitTest, log); + httpClientSubscribe = new HttpClient(pubnubHttpClientHandler); + } + else + { + httpClientSubscribe = new HttpClient(); + } + httpClientSubscribe.DefaultRequestHeaders.Accept.Clear(); + httpClientSubscribe.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpClientSubscribe.Timeout = TimeSpan.FromSeconds(pubnubConfiguation.SubscribeTimeout); + } + if (httpClientNonsubscribe == null) + { + if (pubnubConfiguation.Proxy != null) + { + HttpClientHandler httpClientHandler = new HttpClientHandler(); + if (httpClientHandler.SupportsProxy) + { + httpClientHandler.Proxy = pubnubConfiguation.Proxy; + httpClientHandler.UseProxy = true; + } + pubnubHttpClientHandler = new PubnubHttpClientHandler("PubnubHttpClientHandler", httpClientHandler, pubnubConfiguation, jsonLib, unitTest, log); + httpClientNonsubscribe = new HttpClient(pubnubHttpClientHandler); + } + else + { + httpClientNonsubscribe = new HttpClient(); + } + httpClientNonsubscribe.DefaultRequestHeaders.Accept.Clear(); + httpClientNonsubscribe.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpClientNonsubscribe.Timeout = TimeSpan.FromSeconds(pubnubConfiguation.NonSubscribeRequestTimeout); + } + pubnubHttp = new PubnubHttp(pubnubConfiguation, jsonLib, log, pubnubTelemetryMgr, httpClientSubscribe, httpClientNonsubscribe); +#else + pubnubHttp = new PubnubHttp(pubnubConfiguation, jsonLib, log, pubnubTelemetryMgr); +#endif + + + UpdatePubnubNetworkTcpCheckIntervalInSeconds(); + if (pubnubConfiguation.PresenceInterval > 10) + { + PubnubLocalHeartbeatCheckIntervalInSeconds = pubnubConfiguation.PresenceInterval; + } + enableResumeOnReconnect = pubnubConfiguation.ReconnectionPolicy != PNReconnectionPolicy.NONE; + } + + +#region "Constructors" + + public static bool IsNullOrWhiteSpace(string value) + { + if (string.IsNullOrEmpty(value)) + { + return true; + } + + return string.IsNullOrEmpty(value.Trim()); + } +#endregion + +#region "Internet connection and Reconnect Network" + + protected void ResetInternetCheckSettings(string[] channels, string[] channelGroups) + { + if (channels == null && channelGroups == null) + { + return; + } + + string multiChannel = (channels != null) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ""; + string multiChannelGroup = (channelGroups != null) ? string.Join(",", channelGroups.OrderBy(x => x).ToArray()) : ""; + + if (multiChannel != "") + { + if (ChannelInternetStatus[PubnubInstance.InstanceId].ContainsKey(multiChannel)) + { + ChannelInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(multiChannel, true, (key, oldValue) => true); + } + else + { + ChannelInternetStatus[PubnubInstance.InstanceId].GetOrAdd(multiChannel, true); //Set to true for internet connection + } + } + + if (multiChannelGroup != "") + { + if (ChannelGroupInternetStatus[PubnubInstance.InstanceId].ContainsKey(multiChannelGroup)) + { + ChannelGroupInternetStatus[PubnubInstance.InstanceId].AddOrUpdate(multiChannelGroup, true, (key, oldValue) => true); + } + else + { + ChannelGroupInternetStatus[PubnubInstance.InstanceId].GetOrAdd(multiChannelGroup, true); //Set to true for internet connection + } + } + } + +#endregion + +#region "Callbacks" + + protected bool CheckInternetConnectionStatus(bool systemActive, PNOperationType type, PNCallback callback, string[] channels, string[] channelGroups) + { + PNConfiguration currentConfig; + IPubnubLog currentLog; + pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig); + pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog); + + ClientNetworkStatus.PubnubConfiguation = currentConfig; + ClientNetworkStatus.JsonLibrary = jsonLib; + ClientNetworkStatus.PubnubUnitTest = unitTest; + ClientNetworkStatus.PubnubLog = currentLog; +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + if (httpClientNetworkStatus == null) + { + if (currentConfig.Proxy != null && pubnubHttpClientHandler != null) + { + httpClientNetworkStatus = new HttpClient(pubnubHttpClientHandler); + } + else + { + httpClientNetworkStatus = new HttpClient(); + } + httpClientNetworkStatus.DefaultRequestHeaders.Accept.Clear(); + httpClientNetworkStatus.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpClientNetworkStatus.Timeout = TimeSpan.FromSeconds(currentConfig.NonSubscribeRequestTimeout); + } + ClientNetworkStatus.RefHttpClient = httpClientNetworkStatus; +#endif + if (!ClientNetworkStatus.IsInternetCheckRunning()) + { + clientNetworkStatusInternetStatus = ClientNetworkStatus.CheckInternetStatus(PubnetSystemActive, type, callback, channels, channelGroups); + } + return clientNetworkStatusInternetStatus; + } + + protected static long GetTimetokenFromMultiplexResult(List result) + { + long jsonTimetoken = 0; + Dictionary timetokenObj = jsonLib.ConvertToDictionaryObject(result[0]); + + if (timetokenObj != null && timetokenObj.Count > 0 && timetokenObj.ContainsKey("t")) + { + Dictionary timeAndRegionDictionary = timetokenObj["t"] as Dictionary; + if (timeAndRegionDictionary != null && timeAndRegionDictionary.Count > 0 && timeAndRegionDictionary.ContainsKey("t")) + { + var _ = Int64.TryParse(timeAndRegionDictionary["t"].ToString(), out jsonTimetoken); + } + } + else + { + timetokenObj = jsonLib.ConvertToDictionaryObject(result[1]); + if (timetokenObj != null && timetokenObj.Count > 0 && timetokenObj.ContainsKey("t")) + { + Dictionary timeAndRegionDictionary = timetokenObj["t"] as Dictionary; + if (timeAndRegionDictionary != null && timeAndRegionDictionary.Count > 0 && timeAndRegionDictionary.ContainsKey("t")) + { + var _ = Int64.TryParse(timeAndRegionDictionary["t"].ToString(), out jsonTimetoken); + } + } + else + { + var _ = Int64.TryParse(result[1].ToString(), out jsonTimetoken); + } + } + + return jsonTimetoken; + } + + protected static int GetRegionFromMultiplexResult(List result) + { + int jsonRegion = 0; + Dictionary timetokenObj = jsonLib.ConvertToDictionaryObject(result[0]); + + if (timetokenObj != null && timetokenObj.Count > 0 && timetokenObj.ContainsKey("t")) + { + Dictionary timeAndRegionDictionary = timetokenObj["t"] as Dictionary; + if (timeAndRegionDictionary != null && timeAndRegionDictionary.Count > 0 && timeAndRegionDictionary.ContainsKey("r")) + { + var _ = Int32.TryParse(timeAndRegionDictionary["r"].ToString(), out jsonRegion); + } + } + else + { + timetokenObj = jsonLib.ConvertToDictionaryObject(result[1]); + if (timetokenObj != null && timetokenObj.Count > 0 && timetokenObj.ContainsKey("t")) + { + Dictionary timeAndRegionDictionary = timetokenObj["t"] as Dictionary; + if (timeAndRegionDictionary != null && timeAndRegionDictionary.Count > 0 && timeAndRegionDictionary.ContainsKey("r")) + { + var _ = Int32.TryParse(timeAndRegionDictionary["r"].ToString(), out jsonRegion); + } + } + } + + return jsonRegion; + } + + private static List GetMessageFromMultiplexResult(List result) + { + List jsonMessageList = null; + List msgList = new List(); + + Dictionary messageDicObj = jsonLib.ConvertToDictionaryObject(result[1]); + if (messageDicObj != null && messageDicObj.Count > 0 && messageDicObj.ContainsKey("m")) + { + jsonMessageList = messageDicObj["m"] as List; + } + else + { + messageDicObj = jsonLib.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 = jsonLib.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 = jsonLib.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 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 bool IsZeroTimeTokenRequest(RequestState asyncRequestState, List result) + { + bool ret = false; + PNConfiguration currentConfig = null; + IPubnubLog currentLog = null; + try + { + pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig); + pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog); + + if (asyncRequestState != null && asyncRequestState.ResponseType == PNOperationType.PNSubscribeOperation && result != null && result.Count > 0) + { + List message = GetMessageFromMultiplexResult(result); + if (message != null && message.Count == 0) + { + IEnumerable newChannels = from channel in MultiChannelSubscribe[PubnubInstance.InstanceId] + where channel.Value == 0 + select channel.Key; + IEnumerable newChannelGroups = from channelGroup in MultiChannelGroupSubscribe[PubnubInstance.InstanceId] + where channelGroup.Value == 0 + select channelGroup.Key; + if ((newChannels != null && newChannels.Any()) || (newChannelGroups != null && newChannelGroups.Any())) + { + ret = true; + } + } + } + } + catch (Exception ex) + { + if (currentConfig != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, IsZeroTimeTokenRequest - Exception = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), currentConfig.LogVerbosity); + } + } + return ret; + } + + private void ResponseToConnectCallback(PNOperationType type, string[] channels, string[] channelGroups, RequestState asyncRequestState) + { + PNConfiguration currentConfig; + pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig); + StatusBuilder statusBuilder = new StatusBuilder(currentConfig, jsonLib); + PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNConnectedCategory, asyncRequestState, (int)HttpStatusCode.OK, null); + + //Check callback exists and make sure previous timetoken = 0 + if (channels != null && channels.Length > 0) + { + IEnumerable newChannels = from channel in MultiChannelSubscribe[PubnubInstance.InstanceId] + where channel.Value == 0 + select channel.Key; + if (newChannels != null && newChannels.Any()) + { + status.AffectedChannels = newChannels.ToList(); + } + } + + if (channelGroups != null && channelGroups.Length > 0) + { + IEnumerable newChannelGroups = from channelGroup in MultiChannelGroupSubscribe[PubnubInstance.InstanceId] + where channelGroup.Value == 0 + select channelGroup.Key; + + if (newChannelGroups != null && newChannelGroups.Any()) + { + status.AffectedChannelGroups = newChannelGroups.ToList(); + } + } + + Announce(status); + } + + private void ResponseToUserCallback(List result, PNOperationType type, RequestState asyncRequestState) + { + PNConfiguration currentConfig = null; + IPubnubLog currentLog = null; + try + { + pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig); + pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog); + + var userCallback = (asyncRequestState != null) ? asyncRequestState.PubnubCallback : null; + switch (type) + { + case PNOperationType.PNSubscribeOperation: + case PNOperationType.Presence: + List messageList = GetMessageFromMultiplexResult(result); + if (messageList != null && messageList.Count > 0) + { + if (messageList.Count >= currentConfig.RequestMessageCountThreshold) + { + StatusBuilder statusBuilder = new StatusBuilder(currentConfig, jsonLib); + PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNRequestMessageCountExceededCategory, asyncRequestState, (int)HttpStatusCode.OK, null); + Announce(status); + } + + if (currentConfig != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList.Count = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageList.Count), currentConfig.LogVerbosity); + } + for (int messageIndex = 0; messageIndex < messageList.Count; messageIndex++) + { + SubscribeMessage currentMessage = messageList[messageIndex]; + if (currentMessage != null) + { + if (currentConfig != null && currentLog != null && currentConfig.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), currentConfig.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)), currentConfig.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)), currentConfig.LogVerbosity); + continue; + } + } + else + { + if (currentConfig.CipherKey.Length > 0 && currentMessage.MessageType != 1) //decrypt the subscriber message if cipherkey is available + { + string decryptMessage = ""; + PubnubCrypto aes = new PubnubCrypto(currentConfig.CipherKey, currentConfig, currentLog, null); + try + { + decryptMessage = aes.Decrypt(payload.ToString()); + } + catch (Exception ex) + { + decryptMessage = "**DECRYPT ERROR**"; + + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(ex); + PNStatus status = new StatusBuilder(currentConfig, jsonLib).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 : jsonLib.DeserializeToObject(decryptMessage); + + payloadContainer.Add(decodeMessage); + } + else + { + string payloadJson = jsonLib.SerializeToJsonString(payload); + object payloadJObject = jsonLib.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(currentConfig, jsonLib, 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(currentConfig, jsonLib, currentLog); + PNObjectEventResult objectApiEvent = responseBuilder.JsonToObject(payloadContainer, true); + if (objectApiEvent != null) + { + Announce(objectApiEvent); + } + } + else if (currentMessage.MessageType == 3) + { + ResponseBuilder responseBuilder = new ResponseBuilder(currentConfig, jsonLib, currentLog); + PNMessageActionEventResult msgActionEventEvent = responseBuilder.JsonToObject(payloadContainer, true); + if (msgActionEventEvent != null) + { + Announce(msgActionEventEvent); + } + } + else if (currentMessage.MessageType == 4) + { + ResponseBuilder responseBuilder = new ResponseBuilder(currentConfig, jsonLib, 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() }; + 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 : ""); + 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(currentConfig, jsonLib, currentLog); + PNPresenceEventResult presenceEvent = responseBuilder.JsonToObject(payloadContainer, true); + if (presenceEvent != null) + { + Announce(presenceEvent); + } + } + else + { + if (currentConfig != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - payload = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLib.SerializeToJsonString(payloadContainer)), currentConfig.LogVerbosity); + } + ResponseBuilder responseBuilder = new ResponseBuilder(currentConfig, jsonLib, currentLog); + PNMessageResult userMessage = responseBuilder.JsonToObject>(payloadContainer, true); + if (userMessage != null) + { + Announce(userMessage); + } + } + + } + else + { + if (currentConfig != 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), currentConfig.LogVerbosity); + } + } + } + + } + break; + case PNOperationType.PNTimeOperation: + case PNOperationType.PNPublishOperation: + case PNOperationType.PNFireOperation: + case PNOperationType.PNSignalOperation: + case PNOperationType.PNHistoryOperation: + case PNOperationType.PNFetchHistoryOperation: + case PNOperationType.PNDeleteMessageOperation: + case PNOperationType.PNMessageCountsOperation: + case PNOperationType.PNHereNowOperation: + case PNOperationType.PNWhereNowOperation: + case PNOperationType.PNAccessManagerGrantToken: + case PNOperationType.PNAccessManagerGrant: + case PNOperationType.PNAccessManagerRevokeToken: + case PNOperationType.PNAccessManagerAudit: + case PNOperationType.RevokeAccess: + case PNOperationType.ChannelGroupGrantAccess: + case PNOperationType.ChannelGroupAuditAccess: + case PNOperationType.ChannelGroupRevokeAccess: + case PNOperationType.PNGetStateOperation: + case PNOperationType.PNSetStateOperation: + case PNOperationType.PushRegister: + case PNOperationType.PushRemove: + case PNOperationType.PushGet: + case PNOperationType.PushUnregister: + case PNOperationType.PNAddChannelsToGroupOperation: + case PNOperationType.PNRemoveChannelsFromGroupOperation: + case PNOperationType.PNRemoveGroupOperation: + case PNOperationType.ChannelGroupGet: + case PNOperationType.ChannelGroupAllGet: + case PNOperationType.PNSetUuidMetadataOperation: + case PNOperationType.PNDeleteUuidMetadataOperation: + case PNOperationType.PNGetAllUuidMetadataOperation: + case PNOperationType.PNGetUuidMetadataOperation: + case PNOperationType.PNSetChannelMetadataOperation: + case PNOperationType.PNDeleteChannelMetadataOperation: + case PNOperationType.PNGetAllChannelMetadataOperation: + case PNOperationType.PNGetChannelMetadataOperation: + case PNOperationType.PNGetMembershipsOperation: + case PNOperationType.PNManageMembershipsOperation: + case PNOperationType.PNSetMembershipsOperation: + case PNOperationType.PNRemoveMembershipsOperation: + case PNOperationType.PNGetChannelMembersOperation: + case PNOperationType.PNManageChannelMembersOperation: + case PNOperationType.PNSetChannelMembersOperation: + case PNOperationType.PNRemoveChannelMembersOperation: + case PNOperationType.PNAddMessageActionOperation: + case PNOperationType.PNRemoveMessageActionOperation: + case PNOperationType.PNGetMessageActionsOperation: + case PNOperationType.PNGenerateFileUploadUrlOperation: + case PNOperationType.PNPublishFileMessageOperation: + case PNOperationType.PNListFilesOperation: + case PNOperationType.PNDeleteFileOperation: + if (result != null && result.Count > 0) + { + ResponseBuilder responseBuilder = new ResponseBuilder(currentConfig, jsonLib, currentLog); + T userResult = responseBuilder.JsonToObject(result, true); + + StatusBuilder statusBuilder = new StatusBuilder(currentConfig, jsonLib); + PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNAcknowledgmentCategory, asyncRequestState, (int)HttpStatusCode.OK, null); + + if (userCallback != null) + { + userCallback.OnResponse(userResult, status); + } + } + break; + case PNOperationType.PNHeartbeatOperation: + if (result != null && result.Count > 0) + { + ResponseBuilder responseBuilder = new ResponseBuilder(currentConfig, jsonLib, currentLog); + PNHeartbeatResult userResult = responseBuilder.JsonToObject(result, true); + + if (userResult != null) + { + if (currentConfig.HeartbeatNotificationOption == PNHeartbeatNotificationOption.All) + { + StatusBuilder statusBuilder = new StatusBuilder(currentConfig, jsonLib); + PNStatus status = null; + + PNException ex = null; + if (userResult != null && userResult.Status == 200) + { + status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNAcknowledgmentCategory, asyncRequestState, (int)HttpStatusCode.OK, null); + } + else + { + ex = new PNException(userResult.Message); + status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNAcknowledgmentCategory, asyncRequestState, userResult.Status, ex); + } + + Announce(status); + } + else if (currentConfig.HeartbeatNotificationOption == PNHeartbeatNotificationOption.Failures && userResult.Status != 200) + { + StatusBuilder statusBuilder = new StatusBuilder(currentConfig, jsonLib); + PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNAcknowledgmentCategory, asyncRequestState, userResult.Status, new PNException(userResult.Message)); + Announce(status); + } + } + + } + break; + default: + break; + } + } + catch (Exception ex) + { + if (currentConfig != null && currentLog != null) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - Exception = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), currentConfig.LogVerbosity); + } + } + } + +#endregion + +#region "Simulate network fail and machine sleep" + public static void EnableMachineSleepModeForTestingOnly() + { + PubnetSystemActive = false; + } + + public static void DisableMachineSleepModeForTestingOnly() + { + PubnetSystemActive = true; + } + +#endregion + +#region "Helpers" + + protected string[] GetCurrentSubscriberChannels() + { + string[] channels = null; + if (MultiChannelSubscribe != null && MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.Count > 0) + { + channels = MultiChannelSubscribe[PubnubInstance.InstanceId].Keys.ToArray(); + } + + return channels; + } + + protected string[] GetCurrentSubscriberChannelGroups() + { + string[] channelGroups = null; + if (MultiChannelGroupSubscribe != null && MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId) && MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.Count > 0) + { + channelGroups = MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Keys.ToArray(); + } + + return channelGroups; + } +#endregion + +#region "Build, process and send request" + 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 (terminateCurrentSubRequest) + { + TerminateCurrentSubscriberRequest(); + } + + if (PubnubInstance == null) + { + System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, PubnubInstance is null. Exiting UrlProcessRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture))); + return new Tuple("", null); + } + + if (pubnubRequestState != null) + { + channel = (pubnubRequestState.Channels != null && pubnubRequestState.Channels.Length > 0) ? string.Join(",", pubnubRequestState.Channels.OrderBy(x => x).ToArray()) : ","; + + //if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && !channel.Equals(",", StringComparison.OrdinalIgnoreCase) && !ChannelRequest[PubnubInstance.InstanceId].ContainsKey(channel) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) + //{ + // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + // { + // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, UrlProcessRequest ChannelRequest PubnubInstance.InstanceId Channel NOT matching", DateTime.Now.ToString(CultureInfo.InvariantCulture)), currentConfig.LogVerbosity); + // } + // return new Tuple("", null); + //} + } + +#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; + + if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) + { + ChannelRequest[PubnubInstance.InstanceId].AddOrUpdate(channel, pubnubRequestState.Request, (key, oldState) => pubnubRequestState.Request); + } +#endif + + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Request={1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), requestUri.ToString()), currentConfig.LogVerbosity); + } + + if (pubnubRequestState != null && pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation) + { + SubscribeRequestTracker.AddOrUpdate(PubnubInstance.InstanceId, DateTime.Now, (key, oldState) => DateTime.Now); + } + + 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 + + if (SubscribeDisconnected.ContainsKey(PubnubInstance.InstanceId) && SubscribeDisconnected[PubnubInstance.InstanceId]) + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0},Received JSON but SubscribeDisconnected = {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonString, requestUri), currentConfig.LogVerbosity); + } + throw new OperationCanceledException("Disconnected"); + } + + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, JSON= {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonString, requestUri), currentConfig.LogVerbosity); + } + PNStatus errStatus = GetStatusIfError(pubnubRequestState, jsonString); + if (errStatus == null && pubnubRequestState != null) + { + PNStatus status = new StatusBuilder(currentConfig, jsonLib).CreateStatusResponse(pubnubRequestState.ResponseType, PNStatusCategory.PNAcknowledgmentCategory, pubnubRequestState, (int)HttpStatusCode.OK, null); + return new Tuple(jsonString, status); + } + else + { + jsonString = ""; + return new Tuple(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); + if (PubnubInstance != null && pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + status = new StatusBuilder(currentConfig, jsonLib).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); + } + } + + internal protected async Task> UrlProcessRequestForStream(Uri requestUri, RequestState pubnubRequestState, bool terminateCurrentSubRequest, string contentType) + { + string channel = ""; + PNConfiguration currentConfig; + IPubnubLog currentLog; + + try + { + if (terminateCurrentSubRequest) + { + TerminateCurrentSubscriberRequest(); + } + + if (PubnubInstance == null) + { + System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, PubnubInstance is null. Exiting UrlProcessRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture))); + return new Tuple(null, null); + } + + if (pubnubRequestState != null) + { + channel = (pubnubRequestState.Channels != null && pubnubRequestState.Channels.Length > 0) ? string.Join(",", pubnubRequestState.Channels.OrderBy(x => x).ToArray()) : ","; + + if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && !channel.Equals(",", StringComparison.OrdinalIgnoreCase) && !ChannelRequest[PubnubInstance.InstanceId].ContainsKey(channel) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, UrlProcessRequest ChannelRequest PubnubInstance.InstanceId Channel NOT matching", DateTime.Now.ToString(CultureInfo.InvariantCulture)), currentConfig.LogVerbosity); + } + return new Tuple(null, null); + } + } + +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + //do nothing +#else + // Create Request + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri); + + request = pubnubHttp.SetNoCache(request); + request = pubnubHttp.SetProxy(request); + request = pubnubHttp.SetTimeout(pubnubRequestState, request); + if (string.IsNullOrEmpty(contentType)) + { + contentType = "application/json"; + } + request.ContentType = contentType; + + pubnubRequestState.Request = request; + + if (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence) + { + ChannelRequest[PubnubInstance.InstanceId].AddOrUpdate(channel, pubnubRequestState.Request, (key, oldState) => pubnubRequestState.Request); + } +#endif + + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Request={1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), requestUri.ToString()), currentConfig.LogVerbosity); + } + + if (pubnubRequestState != null && pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation) + { + SubscribeRequestTracker.AddOrUpdate(PubnubInstance.InstanceId, DateTime.Now, (key, oldState) => DateTime.Now); + } + + byte[] streamBytes; +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + streamBytes = await pubnubHttp.SendRequestAndGetStreamResponse(requestUri, pubnubRequestState, null).ConfigureAwait(false); +#else + streamBytes = await pubnubHttp.SendRequestAndGetStreamResponse(requestUri, pubnubRequestState, request).ConfigureAwait(false); +#endif + if (streamBytes != null && pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Stream length= {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), streamBytes.Length, requestUri), currentConfig.LogVerbosity); + } + PNStatus errStatus = GetStatusIfError(pubnubRequestState, null); + if (errStatus == null && pubnubRequestState != null) + { + PNStatus status = new StatusBuilder(currentConfig, jsonLib).CreateStatusResponse(pubnubRequestState.ResponseType, PNStatusCategory.PNAcknowledgmentCategory, pubnubRequestState, (int)HttpStatusCode.OK, null); + return new Tuple(streamBytes, status); + } + else + { + return new Tuple(null, 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); + if (PubnubInstance != null && pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + status = new StatusBuilder(currentConfig, jsonLib).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(null, status); + } + } + internal protected List ProcessJsonResponse(RequestState asyncRequestState, string jsonString) + { + List result = new List(); + + bool errorCallbackRaised = false; + PNStatus status = GetStatusIfError(asyncRequestState, jsonString); + if (status != null) + { + errorCallbackRaised = true; + if (asyncRequestState != null && asyncRequestState.PubnubCallback != null) + { + asyncRequestState.PubnubCallback.OnResponse(default(T), status); + } + else + { + Announce(status); + } + } + if (!errorCallbackRaised && asyncRequestState != null) + { + result = WrapResultBasedOnResponseType(asyncRequestState.ResponseType, jsonString, asyncRequestState.Channels, asyncRequestState.ChannelGroups, asyncRequestState.Reconnect, asyncRequestState.Timetoken, asyncRequestState.PubnubCallback); + } + + return result; + } + + 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 (jsonLib.IsDictionaryCompatible(jsonString, type)) + { + Dictionary deserializeStatus = jsonLib.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)) + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + status = new StatusBuilder(currentConfig, jsonLib).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 = jsonLib.DeserializeToDictionaryOfObject(errorMessageJson); + if (errorDic != null && errorDic.Count > 0 && errorDic.ContainsKey("message") + && statusCode != 200 && pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + string statusMessage = errorDic["message"].ToString(); + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(statusCode, statusMessage); + status = new StatusBuilder(currentConfig, jsonLib).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 = jsonLib.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); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + status = new StatusBuilder(currentConfig, jsonLib).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); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + status = new StatusBuilder(currentConfig, jsonLib).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; + } + protected List WrapResultBasedOnResponseType(PNOperationType type, string jsonString, string[] channels, string[] channelGroups, bool reconnect, long lastTimetoken, PNCallback callback) + { + List result = new List(); + PNConfiguration currentConfig; + IPubnubLog currentLog; + + 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 = jsonLib.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); + + int receivedRegion = GetRegionFromMultiplexResult(result); + LastSubscribeRegion[PubnubInstance.InstanceId] = receivedRegion; + + long receivedTimetoken = GetTimetokenFromMultiplexResult(result); + + long minimumTimetoken1 = (MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Min(token => token.Value) : 0; + long minimumTimetoken2 = (MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Min(token => token.Value) : 0; + long minimumTimetoken = Math.Max(minimumTimetoken1, minimumTimetoken2); + + long maximumTimetoken1 = (MultiChannelSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelSubscribe[PubnubInstance.InstanceId].Max(token => token.Value) : 0; + long maximumTimetoken2 = (MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Count > 0) ? MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Max(token => token.Value) : 0; + long maximumTimetoken = Math.Max(maximumTimetoken1, maximumTimetoken2); + + if (minimumTimetoken == 0 || lastTimetoken == 0) + { + if (maximumTimetoken == 0) + { + LastSubscribeTimetoken[PubnubInstance.InstanceId] = receivedTimetoken; + } + else + { + if (!enableResumeOnReconnect) + { + LastSubscribeTimetoken[PubnubInstance.InstanceId] = receivedTimetoken; + } + else + { + //do nothing. keep last subscribe token + } + } + } + else + { + if (reconnect) + { + if (enableResumeOnReconnect) + { + //do nothing. keep last subscribe token + } + else + { + LastSubscribeTimetoken[PubnubInstance.InstanceId] = receivedTimetoken; + } + } + else + { + LastSubscribeTimetoken[PubnubInstance.InstanceId] = receivedTimetoken; + } + } + break; + case PNOperationType.PNHeartbeatOperation: + Dictionary heartbeatadictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(heartbeatadictionary); + result.Add(multiChannel); + break; + case PNOperationType.PNTimeOperation: + break; + case PNOperationType.PNHistoryOperation: + case PNOperationType.PNFetchHistoryOperation: + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (type == PNOperationType.PNFetchHistoryOperation) + { + for (int index = 0; index < result.Count; index++) + { + Dictionary messageContainer = jsonLib.ConvertToDictionaryObject(result[index]); + if (messageContainer != null && messageContainer.Count > 0) + { + if (messageContainer.ContainsKey("channels")) + { + object channelMessageContainer = messageContainer["channels"]; + Dictionary channelDic = jsonLib.ConvertToDictionaryObject(channelMessageContainer); + if (channelDic != null && channelDic.Count > 0) + { + result[index] = SecureMessage.Instance(currentConfig, jsonLib, currentLog).FetchHistoryDecodeDecryptLoop(type, channelDic, channels, channelGroups, callback); + } + } + else + { + result[index] = messageContainer; + } + } + } + } + else + { + result = SecureMessage.Instance(currentConfig, jsonLib, currentLog).HistoryDecodeDecryptLoop(type, result, channels, channelGroups, callback); + } + } + result.Add(multiChannel); + break; + case PNOperationType.PNMessageCountsOperation: + Dictionary msgCountDictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(msgCountDictionary); + result.Add(multiChannel); + break; + case PNOperationType.PNHereNowOperation: + Dictionary dictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(dictionary); + result.Add(multiChannel); + break; + case PNOperationType.PNWhereNowOperation: + Dictionary whereNowDictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(whereNowDictionary); + result.Add(multiChannel); + break; + case PNOperationType.PNAccessManagerGrantToken: + case PNOperationType.PNAccessManagerGrant: + case PNOperationType.PNAccessManagerRevokeToken: + case PNOperationType.PNAccessManagerAudit: + case PNOperationType.RevokeAccess: + Dictionary grantDictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(grantDictionary); + result.Add(multiChannel); + break; + case PNOperationType.ChannelGroupGrantAccess: + case PNOperationType.ChannelGroupAuditAccess: + case PNOperationType.ChannelGroupRevokeAccess: + Dictionary channelGroupPAMDictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(channelGroupPAMDictionary); + result.Add(multiChannelGroup); + break; + case PNOperationType.PNGetStateOperation: + case PNOperationType.PNSetStateOperation: + Dictionary userStateDictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(userStateDictionary); + result.Add(multiChannelGroup); + result.Add(multiChannel); + break; + case PNOperationType.PNPublishOperation: + case PNOperationType.PNFireOperation: + case PNOperationType.PNSignalOperation: + case PNOperationType.PushRegister: + case PNOperationType.PushRemove: + case PNOperationType.PushGet: + case PNOperationType.PushUnregister: + case PNOperationType.Leave: + case PNOperationType.PNSetUuidMetadataOperation: + case PNOperationType.PNSetChannelMetadataOperation: + case PNOperationType.PNAddMessageActionOperation: + case PNOperationType.PNRemoveMessageActionOperation: + case PNOperationType.PNGetMessageActionsOperation: + case PNOperationType.PNGenerateFileUploadUrlOperation: + case PNOperationType.PNPublishFileMessageOperation: + case PNOperationType.PNListFilesOperation: + case PNOperationType.PNDeleteFileOperation: + result.Add(multiChannel); + break; + case PNOperationType.PNAddChannelsToGroupOperation: + case PNOperationType.PNRemoveChannelsFromGroupOperation: + case PNOperationType.PNRemoveGroupOperation: + case PNOperationType.ChannelGroupGet: + case PNOperationType.ChannelGroupAllGet: + Dictionary channelGroupDictionary = jsonLib.DeserializeToDictionaryOfObject(jsonString); + result = new List(); + result.Add(channelGroupDictionary); + if (multiChannelGroup != "") + { + result.Add(multiChannelGroup); + } + if (multiChannel != "") + { + result.Add(multiChannel); + } + break; + default: + break; + } + //switch stmt end + } + } + catch { /* ignore */ } + + return result; + } + + protected void ProcessResponseCallbacks(List result, RequestState asyncRequestState) + { + bool callbackAvailable = false; + if (result != null && result.Count >= 1 && (asyncRequestState.PubnubCallback != null || SubscribeCallbackListenerList.Count >= 1)) + { + callbackAvailable = true; + } + if (callbackAvailable) + { + bool zeroTimeTokenRequest = IsZeroTimeTokenRequest(asyncRequestState, result); + if (zeroTimeTokenRequest) + { + ResponseToConnectCallback(asyncRequestState.ResponseType, asyncRequestState.Channels, asyncRequestState.ChannelGroups, asyncRequestState); + } + else + { + ResponseToUserCallback(result, asyncRequestState.ResponseType, asyncRequestState); + } + } + } + +#endregion + + 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."); + } + + if (local) + { + if (!string.IsNullOrEmpty(channel) && ChannelLocalUserState[PubnubInstance.InstanceId].ContainsKey(channel)) + { + ChannelLocalUserState[PubnubInstance.InstanceId].TryGetValue(channel, out channelUserStateDictionary); + } + if (!string.IsNullOrEmpty(channelGroup) && ChannelGroupLocalUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) + { + ChannelGroupLocalUserState[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupUserStateDictionary); + } + } + else + { + if (!string.IsNullOrEmpty(channel) && ChannelUserState.ContainsKey(PubnubInstance.InstanceId) && ChannelUserState[PubnubInstance.InstanceId].ContainsKey(channel)) + { + ChannelUserState[PubnubInstance.InstanceId].TryGetValue(channel, out channelUserStateDictionary); + } + if (!string.IsNullOrEmpty(channelGroup)&& ChannelGroupUserState.ContainsKey(PubnubInstance.InstanceId) && ChannelGroupUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) + { + ChannelGroupUserState[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupUserStateDictionary); + } + } + + 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; + } + +#region "Terminate requests and Timers" + + protected void TerminatePendingWebRequest() + { + TerminatePendingWebRequest(null); + } + + protected void TerminatePendingWebRequest(RequestState state) + { + PNConfiguration currentConfig; + IPubnubLog currentLog; + if (state != null && state.Request != null) + { + try + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, TerminatePendingWebRequest - {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), state.Request.RequestUri.ToString()), currentConfig.LogVerbosity); + } + state.Request.Abort(); + } + catch { /* ignore */ } + } + else + { + ICollection keyCollection = ChannelRequest[PubnubInstance.InstanceId].Keys; + foreach (string key in keyCollection) + { + HttpWebRequest currentRequest; + if (ChannelRequest[PubnubInstance.InstanceId].TryGetValue(key, out currentRequest) && currentRequest != null) + { + TerminatePendingWebRequest(currentRequest); + } + } + } + } + + protected static void TerminatePendingWebRequest(HttpWebRequest request) + { + if (request != null) + { + try + { + request.Abort(); + } + catch { /* ignore */ } + } + } + + private void RemoveChannelDictionary() + { + RemoveChannelDictionary(null); + } + + private void RemoveChannelDictionary(RequestState state) + { + PNConfiguration currentConfig; + IPubnubLog currentLog; + + if (state != null && state.Request != null) + { + string channel = (state.Channels != null) ? string.Join(",", state.Channels.OrderBy(x => x).ToArray()) : ","; + + if (ChannelRequest[PubnubInstance.InstanceId].ContainsKey(channel)) + { + HttpWebRequest removedRequest; + bool removeKey = ChannelRequest[PubnubInstance.InstanceId].TryRemove(channel, out removedRequest); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (removeKey) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Remove web request from dictionary in RemoveChannelDictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel), currentConfig.LogVerbosity); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Unable to remove web request from dictionary in RemoveChannelDictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel), currentConfig.LogVerbosity); + } + } + } + } + else + { + ICollection keyCollection = ChannelRequest[PubnubInstance.InstanceId].Keys; + if (keyCollection != null && keyCollection.Count > 0) + { + List keysList = keyCollection.ToList(); + foreach (string key in keysList) + { + HttpWebRequest currentRequest; + if (ChannelRequest[PubnubInstance.InstanceId].TryGetValue(key, out currentRequest) && currentRequest != null) + { + bool removeKey = ChannelRequest[PubnubInstance.InstanceId].TryRemove(key, out currentRequest); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (removeKey) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Remove web request from dictionary in RemoveChannelDictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Unable to remove web request from dictionary in RemoveChannelDictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + } + } + } + } + } + } + + private void RemoveUserState() + { + PNConfiguration currentConfig; + IPubnubLog currentLog; + + if (ChannelLocalUserState.Count == 0 || !ChannelLocalUserState.ContainsKey(PubnubInstance.InstanceId)) { return; } + + ICollection channelLocalUserStateCollection = ChannelLocalUserState[PubnubInstance.InstanceId].Keys; + ICollection channelUserStateCollection = ChannelUserState[PubnubInstance.InstanceId].Keys; + + ICollection channelGroupLocalUserStateCollection = ChannelGroupLocalUserState[PubnubInstance.InstanceId].Keys; + ICollection channelGroupUserStateCollection = ChannelGroupUserState[PubnubInstance.InstanceId].Keys; + + if (channelLocalUserStateCollection != null && channelLocalUserStateCollection.Count > 0) + { + List channelLocalStateList = channelLocalUserStateCollection.ToList(); + foreach (string key in channelLocalStateList) + { + if (ChannelLocalUserState[PubnubInstance.InstanceId].ContainsKey(key)) + { + Dictionary tempUserState; + bool removeKey = ChannelLocalUserState[PubnubInstance.InstanceId].TryRemove(key, out tempUserState); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (removeKey) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} RemoveUserState from local user state dictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Unable to RemoveUserState from local user state dictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + } + } + } + } + + if (channelUserStateCollection != null && channelUserStateCollection.Count > 0) + { + List channelStateList = channelUserStateCollection.ToList(); + foreach (string key in channelStateList) + { + if (ChannelUserState[PubnubInstance.InstanceId].ContainsKey(key)) + { + Dictionary tempUserState; + bool removeKey = ChannelUserState[PubnubInstance.InstanceId].TryRemove(key, out tempUserState); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (removeKey) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} RemoveUserState from user state dictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Unable to RemoveUserState from user state dictionary for channel= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + } + } + } + } + + if (channelGroupLocalUserStateCollection != null && channelGroupLocalUserStateCollection.Count > 0) + { + List channelGroupLocalStateList = channelGroupLocalUserStateCollection.ToList(); + foreach (string key in channelGroupLocalStateList) + { + if (ChannelGroupLocalUserState[PubnubInstance.InstanceId].ContainsKey(key)) + { + Dictionary tempUserState; + bool removeKey = ChannelGroupLocalUserState[PubnubInstance.InstanceId].TryRemove(key, out tempUserState); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (removeKey) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} RemoveUserState from local user state dictionary for channelgroup= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Unable to RemoveUserState from local user state dictionary for channelgroup= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + } + } + } + } + + if (channelGroupUserStateCollection != null && channelGroupUserStateCollection.Count > 0) + { + List channelGroupStateList = channelGroupUserStateCollection.ToList(); + + foreach (string key in channelGroupStateList) + { + if (ChannelGroupUserState[PubnubInstance.InstanceId].ContainsKey(key)) + { + Dictionary tempUserState; + bool removeKey = ChannelGroupUserState[PubnubInstance.InstanceId].TryRemove(key, out tempUserState); + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + if (removeKey) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} RemoveUserState from user state dictionary for channelgroup= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + else + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} Unable to RemoveUserState from user state dictionary for channelgroup= {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + } + } + } + } + } + + protected static void TerminatePresenceHeartbeatTimer() + { + if (PresenceHeartbeatTimer != null) + { + PresenceHeartbeatTimer.Dispose(); + PresenceHeartbeatTimer = null; + } + } + + protected static void TerminateTelemetry() + { + if (pubnubTelemetryMgr != null) + { + pubnubTelemetryMgr.Destroy(); + } + } + + protected void TerminateTokenManagerCollection() + { + if (PubnubTokenMgrCollection != null && PubnubTokenMgrCollection.Count > 0) + { + List tokenMgrList = PubnubTokenMgrCollection.Keys.ToList(); + foreach(string key in tokenMgrList) + { + if (PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) + { + PubnubTokenMgrCollection[PubnubInstance.InstanceId].Destroy(); + } + } + } + } + + protected static void TerminateDedupeManager() + { + if (pubnubSubscribeDuplicationManager != null) + { + pubnubSubscribeDuplicationManager.ClearHistory(); + pubnubSubscribeDuplicationManager = null; + } + } + + protected void UpdatePubnubNetworkTcpCheckIntervalInSeconds() + { + int timerInterval; + PNConfiguration currentConfig; + IPubnubLog currentLog; + if (PubnubInstance != null && pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig)) + { + if (currentConfig.ReconnectionPolicy == PNReconnectionPolicy.EXPONENTIAL) + { + timerInterval = (int)(Math.Pow(2, ConnectionErrors) - 1); + if (timerInterval > MAXEXPONENTIALBACKOFF) + { + timerInterval = MINEXPONENTIALBACKOFF; + ConnectionErrors = 1; + if (pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, EXPONENTIAL timerInterval > MAXEXPONENTIALBACKOFF", DateTime.Now.ToString(CultureInfo.InvariantCulture)), currentConfig.LogVerbosity); + } + } + else if (timerInterval < 1) + { + timerInterval = MINEXPONENTIALBACKOFF; + } + if (pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, EXPONENTIAL timerInterval = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), timerInterval.ToString(CultureInfo.InvariantCulture)), currentConfig.LogVerbosity); + } + } + else if (currentConfig.ReconnectionPolicy == PNReconnectionPolicy.LINEAR) + { + timerInterval = INTERVAL; + } + else + { + timerInterval = -1; + } + + PubnubNetworkTcpCheckIntervalInSeconds = timerInterval; + } + } + + protected void TerminateReconnectTimer() + { + PNConfiguration currentConfig; + IPubnubLog currentLog; + try + { + if (string.IsNullOrEmpty(PubnubInstance.InstanceId) || ChannelReconnectTimer == null || ChannelReconnectTimer.Count == 0 || !ChannelReconnectTimer.ContainsKey(PubnubInstance.InstanceId)) + { + return; + } + + ConcurrentDictionary channelReconnectCollection = ChannelReconnectTimer[PubnubInstance.InstanceId]; + ICollection keyCollection = channelReconnectCollection.Keys; + if (keyCollection != null && keyCollection.Count > 0) + { + List keyList = keyCollection.ToList(); + foreach (string key in keyList) + { + if (ChannelReconnectTimer[PubnubInstance.InstanceId].ContainsKey(key)) + { + try + { + Timer currentTimer; + if (ChannelReconnectTimer[PubnubInstance.InstanceId].TryGetValue(key, out currentTimer)) + { + currentTimer.Change(Timeout.Infinite, Timeout.Infinite); + currentTimer.Dispose(); + } + } + catch { /* ignore */ } + + Timer removedTimer = null; + bool removed = ChannelReconnectTimer[PubnubInstance.InstanceId].TryRemove(key, out removedTimer); + if (!removed) + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} TerminateReconnectTimer(null) - Unable to remove channel reconnect timer reference from collection for {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), key), currentConfig.LogVerbosity); + } + } + } + } + } + + ICollection groupKeyCollection = null; + if (ChannelGroupReconnectTimer.Count > 0 && ChannelGroupReconnectTimer.ContainsKey(PubnubInstance.InstanceId)) + { + ConcurrentDictionary channelGroupReconnectCollection = ChannelGroupReconnectTimer[PubnubInstance.InstanceId]; + groupKeyCollection = channelGroupReconnectCollection.Keys; + } + if (groupKeyCollection != null && groupKeyCollection.Count > 0) + { + List groupKeyList = groupKeyCollection.ToList(); + foreach (string groupKey in groupKeyList) + { + if (ChannelGroupReconnectTimer[PubnubInstance.InstanceId].ContainsKey(groupKey)) + { + try + { + Timer currentTimer; + if (ChannelGroupReconnectTimer[PubnubInstance.InstanceId].TryGetValue(groupKey, out currentTimer)) + { + currentTimer.Change(Timeout.Infinite, Timeout.Infinite); + currentTimer.Dispose(); + } + } + catch { /* ignore */ } + + Timer removedTimer = null; + bool removed = ChannelGroupReconnectTimer[PubnubInstance.InstanceId].TryRemove(groupKey, out removedTimer); + if (!removed) + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} TerminateReconnectTimer(null) - Unable to remove channelgroup reconnect timer reference from collection for {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), groupKey), currentConfig.LogVerbosity); + } + } + } + } + } + } + catch (Exception ex) + { + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} TerminateReconnectTimer exception: {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), currentConfig.LogVerbosity); + } + } + } + + protected bool DeleteLocalChannelUserState(string channel) + { + bool userStateDeleted = false; + + if (ChannelLocalUserState[PubnubInstance.InstanceId].ContainsKey(channel)) + { + Dictionary returnedUserState = null; + userStateDeleted = ChannelLocalUserState[PubnubInstance.InstanceId].TryRemove(channel, out returnedUserState); + } + + return userStateDeleted; + } + + protected bool DeleteLocalChannelGroupUserState(string channelGroup) + { + bool userStateDeleted = false; + + if (ChannelGroupLocalUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) + { + Dictionary returnedUserState = null; + userStateDeleted = ChannelGroupLocalUserState[PubnubInstance.InstanceId].TryRemove(channelGroup, out returnedUserState); + } + + return userStateDeleted; + } + + internal void EndPendingRequests() + { + if (SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + SubscribeCallbackListenerList[PubnubInstance.InstanceId].Clear(); + } + + RemoveChannelDictionary(); + TerminatePendingWebRequest(); + TerminateReconnectTimer(); + RemoveUserState(); + PubnubCoreBase2.TerminatePresenceHeartbeatTimer(); + TerminateTelemetry(); + TerminateDedupeManager(); + TerminateTokenManagerCollection(); + + if (MultiChannelSubscribe.Count > 0 && MultiChannelSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + MultiChannelSubscribe[PubnubInstance.InstanceId].Clear(); + } + + if (MultiChannelGroupSubscribe.Count > 0 && MultiChannelGroupSubscribe.ContainsKey(PubnubInstance.InstanceId)) + { + MultiChannelGroupSubscribe[PubnubInstance.InstanceId].Clear(); + } + + if (ChannelLocalUserState.Count > 0 && ChannelLocalUserState.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelLocalUserState[PubnubInstance.InstanceId].Clear(); + } + + if (ChannelUserState.Count > 0 && ChannelUserState.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelUserState[PubnubInstance.InstanceId].Clear(); + } + + if (ChannelGroupLocalUserState.Count > 0 && ChannelGroupLocalUserState.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelGroupLocalUserState[PubnubInstance.InstanceId].Clear(); + } + + if (ChannelGroupUserState.Count > 0 && ChannelGroupUserState.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelGroupUserState[PubnubInstance.InstanceId].Clear(); + } + + if (MultiChannelSubscribe.Count > 0 && !MultiChannelSubscribe.Where(t => t.Value.Keys.Count > 0).Any() + && MultiChannelGroupSubscribe.Count > 0 && !MultiChannelGroupSubscribe.Where(t => t.Value.Keys.Count > 0).Any()) + { + RemoveHttpClients(); + } + PubnubInstance = null; + } + + internal static void RemoveHttpClients() + { + //Conditionalmethod logic +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + if (httpClientNetworkStatus != null) + { + try{ + httpClientNetworkStatus.CancelPendingRequests(); + httpClientNetworkStatus.Dispose(); + httpClientNetworkStatus = null; + } + catch { /* ignore */ } + } + if (httpClientSubscribe != null) + { + try{ + httpClientSubscribe.CancelPendingRequests(); + httpClientSubscribe.Dispose(); + httpClientSubscribe = null; + } + catch { /* ignore */ } + } + if (httpClientNonsubscribe != null) + { + try{ + httpClientNonsubscribe.CancelPendingRequests(); + httpClientNonsubscribe.Dispose(); + httpClientNonsubscribe = null; + } + catch { /* ignore */ } + } +#endif + } + + internal void TerminateCurrentSubscriberRequest() + { + string[] channels = GetCurrentSubscriberChannels(); + if (channels != null) + { + string multiChannel = (channels.Length > 0) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ","; + HttpWebRequest request; + if (ChannelRequest[PubnubInstance.InstanceId].ContainsKey(multiChannel) && ChannelRequest[PubnubInstance.InstanceId].TryGetValue(multiChannel, out request) && request != null) + { + PNConfiguration currentConfig; + IPubnubLog currentLog; + if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) + { + LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} TerminateCurrentSubsciberRequest {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), request.RequestUri.ToString()), currentConfig.LogVerbosity); + } + try + { + request.Abort(); + } + catch { /* ignore */ } + } + } +#if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 + if (httpClientSubscribe != null) + { + try + { + httpClientSubscribe.CancelPendingRequests(); + } + catch { /* ignore */ } + } +#endif + } + + #endregion + + internal void Announce(PNStatus status) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Status(PubnubInstance, status); + } + } + + } + + internal void Announce(PNMessageResult message) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Message(PubnubInstance, message); + } + } + } + + internal void Announce(PNSignalResult message) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Signal(PubnubInstance, message); + } + } + } + + internal void Announce(PNFileEventResult message) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].File(PubnubInstance, message); + } + } + } + + internal void Announce(PNPresenceEventResult presence) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].Presence(PubnubInstance, presence); + } + } + } + + internal void Announce(PNObjectEventResult objectApiEvent) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].ObjectEvent(PubnubInstance, objectApiEvent); + } + } + } + + internal void Announce(PNMessageActionEventResult messageActionEvent) + { + if (PubnubInstance != null && SubscribeCallbackListenerList.ContainsKey(PubnubInstance.InstanceId)) + { + List callbackList = SubscribeCallbackListenerList[PubnubInstance.InstanceId]; + for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) + { + callbackList[listenerIndex].MessageAction(PubnubInstance, messageActionEvent); + } + } + } + + internal void InitializeDefaultVariableObjectStates() + { + if (!ChannelRequest.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelRequest.GetOrAdd(PubnubInstance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelInternetStatus.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelInternetStatus.GetOrAdd(PubnubInstance.InstanceId, new ConcurrentDictionary()); + } + if (!ChannelGroupInternetStatus.ContainsKey(PubnubInstance.InstanceId)) + { + ChannelGroupInternetStatus.GetOrAdd(PubnubInstance.InstanceId, new ConcurrentDictionary()); + } + + } + } +} diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index fe0f9c53b..eaa8893cf 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -2,6 +2,7 @@ netstandard1.0;netstandard1.3;netstandard1.4;netstandard1.1;netstandard2.0;net6.0 + 8.0 true True pubnub.snk @@ -149,9 +150,11 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. EndPoint\PubSub\SubscribeManager.cs + EndPoint\PubSub\SubscribeOperation.cs + EndPoint\PubSub\UnsubscribeAllOperation.cs @@ -211,6 +214,14 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. Enum\ResponseType.cs + + + + + + + + HttpUtility\HttpUtility.cs @@ -513,6 +524,7 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. PubnubCoreBase.cs + PubnubHttp.cs @@ -885,15 +897,12 @@ Added TcpKeepAlive and ConnectionLimit to improve performance. + - - - - - PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously - PubNub 2012-2021 - false - - - - - - 0.3.1 - None - - - 1.0.2856 - None - - - 1.0.2856 - None - - - None - - - - - - - 0.3.1 - None - - - None - - - - - - - - None - - - - - - None - - - - - - None - - - - - - - None - - - - - - - None - - - - - - - None - - - - - - - None - - - - - - None - - - - - - None - - - None - - - None - - - None - - - None - - - None - - - None - - - None - - - - - - None - - - None - - - None - - - None - - - None - - - None - - - None - - - None - - - - diff --git a/src/PNEventEngine/ReceivingEffectHandler.cs b/src/PNEventEngine/ReceivingEffectHandler.cs deleted file mode 100644 index d1ba3b35b..000000000 --- a/src/PNEventEngine/ReceivingEffectHandler.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace PNEventEngine -{ - public class ReceiveingResponse - { - [JsonProperty("t")] - public Timetoken? Timetoken { get; set; } - - [JsonProperty("m")] - public object[]? Messages { get; set; } - } - - public class ReceivingEffectHandler : IEffectHandler - { - EventEmitter emitter; - //HttpClient httpClient; - CancellationTokenSource? cancellationTokenSource; - - //PNConfiguration pnConfig; - public ReceivingEffectHandler(Action httpRequestHandler, EventEmitter emitter) - { - this.emitter = emitter; - //httpClient = client; - //pnConfig = config; - cancellationTokenSource = new CancellationTokenSource(); - } - - public async void Start(ExtendedState context) - { - await Task.Factory.StartNew(() => { }); - if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { - Cancel(); - } - cancellationTokenSource = new CancellationTokenSource(); - var evnt = new Event(); - // TODO: Replace with stateless Utility method... - try { - //var res = await httpClient.GetAsync($"https://ps.pndsn.com/v2/subscribe/sub-c-c9710928-1b7a-11e3-a0c8-02ee2ddab7fe/{String.Join(",", context.Channels.ToArray())}/0?uuid=cSharpTest&channel-group={String.Join(",", context.ChannelGroups.ToArray())}&tt={context.Timetoken}&tr={context.Region}", cancellationTokenSource.Token); - //string jsonResp = await res.Content.ReadAsStringAsync(); - ////LoggingMethod.WriteToLog(string.Format("ReceivingEffectHandler - {0}",jsonResp)); - //var receivedResponse = JsonConvert.DeserializeObject(jsonResp); - //evnt.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; - //evnt.EventPayload.Region = receivedResponse.Timetoken.Region; - //evnt.Type = EventType.ReceiveSuccess; - - //if (receivedResponse.Messages != null) - // Console.WriteLine($"Received Messages {JsonConvert.SerializeObject(receivedResponse.Messages)}"); //WIP: Define "DELIVERING" Effect. and transition - - } catch (Exception ex) { - evnt.Type = EventType.ReceiveFailed; - //LoggingMethod.WriteToLog(string.Format("ReceivingEffectHandler EXCEPTION - {0}",ex)); - evnt.EventPayload.exception = ex; - } - emitter.emit(evnt); - } - public void Cancel() - { - //Console.WriteLine("Attempting cancellation"); - //LoggingMethod.WriteToLog("ReceivingEffectHandler - Attempting cancellation"); - if (cancellationTokenSource != null) - { - cancellationTokenSource.Cancel(); - } - } - } -} diff --git a/src/PNEventEngine/ReconnectingEffectHandler.cs b/src/PNEventEngine/ReconnectingEffectHandler.cs deleted file mode 100644 index a5758e953..000000000 --- a/src/PNEventEngine/ReconnectingEffectHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace PNEventEngine -{ - public class ReconnectingEffectHandler : IEffectHandler - { - EventEmitter eventEmitter; - - //PNConfiguration pnConfig; - public ReconnectingEffectHandler(Action httpRequestHandler, EventEmitter emitter) - { - this.eventEmitter = emitter; - //pnConfig = config; - } - - public void Start(ExtendedState context) // TODO: Implementation of retry getDelay() as per policy - { - var evnt = new Event(); - evnt.EventPayload.Timetoken = context.Timetoken; - evnt.EventPayload.Region = context.Region; - evnt.Type = EventType.ReceiveSuccess; - this.eventEmitter.emit(evnt); - } - - public void Cancel() - { - System.Diagnostics.Debug.WriteLine("Reconnecting Cancelled!!!"); - } - } -} diff --git a/src/PNEventEngine/State.cs b/src/PNEventEngine/State.cs deleted file mode 100644 index 47448415e..000000000 --- a/src/PNEventEngine/State.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PNEventEngine -{ - - public enum StateType { Unsubscribed, Handshaking, Receiving, HandshakingFailed, ReconnectingFailed, Reconnecting }; - - public class State - { - public StateType Type { get; set; } - public Dictionary transitions; - public List Effects { get; set; } - public Func Entry { get; set; } = () => { - return true; - }; - - public Func Exit { get; set; } = () => { - return true; - }; - - public State(StateType type) - { - this.Type = type; - this.transitions = new Dictionary(); - Effects = new List(); - } - - public State On(EventType e, StateType nextState) - { - transitions.Add(e, nextState); - return this; - } - - public State OnEntry(Func entry) - { - this.Entry = entry; - return this; - } - - public State OnExit(Func exit) - { - this.Exit = exit; - return this; - } - - public State Effect(EffectType effect) - { - this.Effects.Add(effect); - return this; - } - } -} diff --git a/src/PNEventEngine/pubnub.snk b/src/PNEventEngine/pubnub.snk deleted file mode 100644 index ab6f98e6ccbe10048ac6dfc9922832a5c4b39a22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096=h*pb2CXpvv;Js;9b9kQs|3b=ComT{N ziUL7K9M}p!WAqV04_SQXqzw;R;bq54Xnywpy`2cq;aP9P6vK}{hq(oAm@7=O7(xoP zY}{Hc&8AbMXN0npkl4STLH<s^7fZ z@G104xb8*?e}bGrE&T#EGhb0ocj6KeeXb$DF{GaDvCq4&h7(OV(!{x~o|bHSlV7^| zYGMl`=Lx$CL!#xY((Qw3Urdawedl{1sf}?e9lj{n(#+}o*+?P|*NTx5C|O{NdRMOo z87;7|v4snR2zBjV|4Qh0`ku#5QjgMyS*v<3vRA(U%7OKwYjO064<=cZ28s>#-RxXL4I#C2%<%<-zIIrTY)8c*(A?p9hA)oTMXkCTlb(Wf5axkzrW%&T zoJj}GHDWELO-QEm6L}-9Q=xdCRkmb#< zm+&3vUMMt5!+Rx)1B9~r#!8#o5WY*jh0;VT@ygH53);jOQ6>PZOhQO1K6(G5{3|HU iSa<~)<|H!5yTV$GoUpH<7?8K2GQo*km!(;|inuqU - - - - Debug - AnyCPU - {E47B5226-5A59-484B-BAA6-19BBD7C5BACD} - Library - Properties - PNEventEngineUWP - PNEventEngineUWP - en-US - UAP - 10.0.22000.0 - 10.0.10586.0 - 14 - 512 - {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - prompt - 4 - - - x86 - true - bin\x86\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - false - prompt - - - x86 - bin\x86\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - false - prompt - - - ARM - true - bin\ARM\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - false - prompt - - - ARM - bin\ARM\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - false - prompt - - - ARM64 - true - bin\ARM64\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - false - prompt - - - ARM64 - bin\ARM64\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - false - prompt - - - x64 - true - bin\x64\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - ;2008 - full - false - prompt - - - x64 - bin\x64\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - true - ;2008 - pdbonly - false - prompt - - - PackageReference - disable - enable - 8.0 - - - - - EffectDispatcher.cs - - - EventEmitter.cs - - - EventEngine.cs - - - HandshakeEffectHandler.cs - - - IEffectHandler.cs - - - ReceivingEffectHandler.cs - - - ReconnectingEffectHandler.cs - - - State.cs - - - - - - - None - - - - - - 6.2.14 - - - - 14.0 - - - - \ No newline at end of file diff --git a/src/PNEventEngineUWP/Properties/AssemblyInfo.cs b/src/PNEventEngineUWP/Properties/AssemblyInfo.cs deleted file mode 100644 index d6c1417c8..000000000 --- a/src/PNEventEngineUWP/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("PNEventEngineUWP")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PNEventEngineUWP")] -[assembly: AssemblyCopyright("Copyright © 2023")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml b/src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml deleted file mode 100644 index e8b9c7f98..000000000 --- a/src/PNEventEngineUWP/Properties/PNEventEngineUWP.rd.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - From 0c793ba975f3b71916e3db3a048b64417432328b Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 22 May 2023 19:38:34 +0530 Subject: [PATCH 30/71] Refactored Effect Dispatcher --- .../EndPoint/PubSub/SubscribeOperation2.cs | 2 + .../PubnubApi/EventEngine/EffectDispatcher.cs | 44 +++++++++++++- src/Api/PubnubApi/EventEngine/EventEngine.cs | 59 ++----------------- 3 files changed, 50 insertions(+), 55 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 6cd9ac92c..25b6da827 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -46,6 +46,8 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j var effectDispatcher = new EffectDispatcher(); + effectDispatcher.PubnubUnitTest = unit; + var eventEmitter = new EventEmitter(); eventEmitter.RegisterJsonListener(JsonCallback); diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs index d3f0e19e0..49bd6803f 100644 --- a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -1,11 +1,21 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.Remoting.Contexts; using System.Threading.Tasks; namespace PubnubApi.PubnubEventEngine { public class EffectDispatcher { + public enum DispatcherType + { + Entry, + Exit, + Managed + } + public IPubnubUnitTest PubnubUnitTest { get; set; } + public Dictionary effectActionMap; public EffectDispatcher() { @@ -18,11 +28,43 @@ public async void dispatch(EventType effect, ExtendedState stateContext) if (effectActionMap.TryGetValue(effect, out handler)) { if (handler != null) { - await Task.Factory.StartNew(()=> handler.Start(stateContext, effect)).ConfigureAwait(false);; + await Task.Factory.StartNew(()=> handler.Start(stateContext, effect)).ConfigureAwait(false); } } } + public async void dispatch(DispatcherType dispatchType, EventType eventType,List effectInvocations, ExtendedState stateContext) + { + foreach (var effect in effectInvocations) { + PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", effect.Name)); + System.Diagnostics.Debug.WriteLine("Found effect " + effect.Effectype); + if (dispatchType == DispatcherType.Exit) + { + await Task.Factory.StartNew(()=> effect.Handler?.Cancel()).ConfigureAwait(false); + } + else if (dispatchType == DispatcherType.Entry) + { + await Task.Factory.StartNew(()=> effect.Handler?.Start(stateContext, eventType)).ConfigureAwait(false); + } + else if (dispatchType == DispatcherType.Managed) + { + if (effect is EmitStatus) + { + ((EmitStatus)effect).Announce(); + } + else if (effect is EmitMessages) + { + ((EmitMessages)effect).Announce(); + } + else if (effect is EmitMessages) + { + ((EmitMessages)effect).Announce(); + } + } + } + + } + public void Register(EventType type, IEffectInvocationHandler handler) { effectActionMap.Add(type, handler); diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 5f2bcf545..d5ce6c5a9 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -329,12 +329,9 @@ public void Transition(Event e) { if (CurrentState != null) { State findState = States.Find((s) => s.StateType == CurrentState.StateType); - //State nextState; StateType nextStateType; if (findState != null && findState.transitions != null && findState.transitions.TryGetValue(e.EventType, out nextStateType)) { - //StateType nextStateType = findState.transitions[CurrentState.EventType]; - // System.Diagnostics.Debug.WriteLine($"Current State = {CurrentState.StateType}; Transition = {e.EventType}"); if (PubnubUnitTest != null ) { @@ -345,36 +342,16 @@ public void Transition(Event e) { if (findState.ExitList != null && findState.ExitList.Count > 0) { - foreach(var entry in findState.ExitList) - { - PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", entry.Name)); - entry.Handler?.Cancel(); - } + Dispatcher.dispatch(EffectDispatcher.DispatcherType.Exit, 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]; - foreach (var effect in effectInvocationList) { - PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", effect.Name)); - if (effect is EmitStatus) - { - ((EmitStatus)effect).Announce(); - } - else if (effect is EmitMessages) - { - ((EmitMessages)effect).Announce(); - } - else if (effect is EmitMessages) - { - ((EmitMessages)effect).Announce(); - } - System.Diagnostics.Debug.WriteLine("Found effect " + effect.Effectype); - Dispatcher.dispatch(effect.Effectype, this.Context); - //if (e.EventType == effect.Effectype) - //{ - //} + if (effectInvocationList != null && effectInvocationList.Count > 0) + { + Dispatcher.dispatch(EffectDispatcher.DispatcherType.Managed, e.EventType, effectInvocationList, this.Context); } } @@ -382,36 +359,10 @@ public void Transition(Event e) UpdateContext(e.EventType, e.EventPayload); if (CurrentState.EntryList != null && CurrentState.EntryList.Count > 0) { - foreach(var entry in CurrentState.EntryList) - { - PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", entry.Name)); - entry.Handler?.Start(Context, e.EventType); - } + Dispatcher.dispatch(EffectDispatcher.DispatcherType.Entry, e.EventType, findState.EntryList, this.Context); } - //findState.EventType = e.EventType; - //CurrentState = findState; UpdateContext(e.EventType, e.EventPayload); - //CurrentState = NextState(); - //if (CurrentState != null) - //{ - // System.Diagnostics.Debug.WriteLine($"Next State = {CurrentState.StateType}; Transition = {e.EventType}"); - // UpdateContext(e.EventType, e.EventPayload); - // //System.Diagnostics.Debug.WriteLine($"Emitting event { e.EventType }"); - // //Emitter.emit(e); - //} - if (findState != null) - { - //if (CurrentState.EffectInvocationsList[e.EventType].Count > 0) { - // foreach (var effect in CurrentState.EffectInvocationsList) { - // if (e.EventType == effect.Effectype) - // { - // System.Diagnostics.Debug.WriteLine("Found effect "+ effect.Effectype); - // Dispatcher.dispatch(effect.Effectype, this.Context); - // } - // } - //} - } } } From 3666c9da07caaa70c696455ce2c9dafd9205eb3a Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 22 May 2023 19:59:56 +0530 Subject: [PATCH 31/71] fix in refactored effect dispatcher --- src/Api/PubnubApi/EventEngine/EffectDispatcher.cs | 3 +-- src/Api/PubnubApi/EventEngine/EventEngine.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs index 49bd6803f..7c6801f39 100644 --- a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Runtime.Remoting.Contexts; using System.Threading.Tasks; namespace PubnubApi.PubnubEventEngine @@ -35,6 +33,7 @@ public async void dispatch(EventType effect, ExtendedState stateContext) public async void dispatch(DispatcherType dispatchType, EventType eventType,List effectInvocations, ExtendedState stateContext) { + if (effectInvocations == null || effectInvocations.Count == 0) { return; } foreach (var effect in effectInvocations) { PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", effect.Name)); System.Diagnostics.Debug.WriteLine("Found effect " + effect.Effectype); diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index d5ce6c5a9..8b9cfb924 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -359,7 +359,7 @@ public void Transition(Event e) UpdateContext(e.EventType, e.EventPayload); if (CurrentState.EntryList != null && CurrentState.EntryList.Count > 0) { - Dispatcher.dispatch(EffectDispatcher.DispatcherType.Entry, e.EventType, findState.EntryList, this.Context); + Dispatcher.dispatch(EffectDispatcher.DispatcherType.Entry, e.EventType, CurrentState.EntryList, this.Context); } UpdateContext(e.EventType, e.EventPayload); From 8e49353979c9dfd281d16180e1655ea258f8282d Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 22 May 2023 20:06:37 +0530 Subject: [PATCH 32/71] Updated dependency nuget packages --- .../AcceptanceTests/AcceptanceTests.csproj | 12 +- .../Features/channel-metadata.feature.cs | 112 +++----------- .../event-engine/happy-path.feature.cs | 79 +++------- .../Features/grant-token.feature.cs | 127 ++++----------- .../Features/members-metadata.feature.cs | 112 +++----------- .../Features/membership-metadata.feature.cs | 144 ++++-------------- .../Features/revoke-token.feature.cs | 80 +++------- .../Features/uuid-metadata.feature.cs | 128 ++++------------ .../AcceptanceTests/Steps/EventEngineSteps.cs | 5 +- 9 files changed, 184 insertions(+), 615 deletions(-) diff --git a/src/UnitTests/AcceptanceTests/AcceptanceTests.csproj b/src/UnitTests/AcceptanceTests/AcceptanceTests.csproj index b5169b946..4ca2f65a3 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.cs b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs index e2ebea9ad..841b19aba 100644 --- a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs @@ -27,7 +27,7 @@ public partial class EventEngineFeature private TechTalk.SpecFlow.ITestRunner testRunner; - private string[] _featureTags = new string[] { + private static string[] featureTags = new string[] { "featureSet=eventEngine"}; #line 1 "happy-path.feature" @@ -37,8 +37,7 @@ public partial class EventEngineFeature 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, new string[] { - "featureSet=eventEngine"}); + 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); } @@ -50,28 +49,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(); } @@ -89,27 +88,17 @@ public virtual void FeatureBackground() [NUnit.Framework.DescriptionAttribute("Successfully receive messages")] [NUnit.Framework.CategoryAttribute("contract=simpleSubscribe")] [NUnit.Framework.CategoryAttribute("beta")] - public virtual void SuccessfullyReceiveMessages() + 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, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Successfully receive messages", 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(); } @@ -172,27 +161,17 @@ public virtual void SuccessfullyReceiveMessages() [NUnit.Framework.DescriptionAttribute("Complete handshake failure")] [NUnit.Framework.CategoryAttribute("contract=subscribeHandshakeFailure")] [NUnit.Framework.CategoryAttribute("beta")] - public virtual void CompleteHandshakeFailure() + 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, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Complete handshake failure", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 27 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(); } @@ -276,27 +255,17 @@ public virtual void CompleteHandshakeFailure() [NUnit.Framework.DescriptionAttribute("Handshake failure recovery")] [NUnit.Framework.CategoryAttribute("contract=subscribeHandshakeRecovery")] [NUnit.Framework.CategoryAttribute("beta")] - public virtual void HandshakeFailureRecovery() + 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, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Handshake failure recovery", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 52 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(); } @@ -377,27 +346,17 @@ public virtual void HandshakeFailureRecovery() [NUnit.Framework.DescriptionAttribute("Receiving failure recovery")] [NUnit.Framework.CategoryAttribute("contract=subscribeReceivingRecovery")] [NUnit.Framework.CategoryAttribute("beta")] - public virtual void ReceivingFailureRecovery() + 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, this._featureTags); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Receiving failure recovery", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 76 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/grant-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs index 299ba3b92..a1aaaf549 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" @@ -39,8 +39,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", "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"}); + "devices) which are each\r\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..d5a85c045 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"}; @@ -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", "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"}); + "Nub 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 index 5e2376aec..9b2edae6b 100644 --- a/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs +++ b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs @@ -13,6 +13,7 @@ using System.Threading; using PubnubApi.PubnubEventEngine; using TechTalk.SpecFlow.Assist; +using System.Net.Http; namespace AcceptanceTests.Steps { @@ -187,8 +188,8 @@ public void BeforeScenario() { string mockInitContract = string.Format("http://{0}/init?__contract__script__={1}", acceptance_test_origin, currentContract); System.Diagnostics.Debug.WriteLine(mockInitContract); - WebClient webClient = new WebClient(); - string mockInitResponse = webClient.DownloadString(mockInitContract); + HttpClient httpclient = new HttpClient(); + string mockInitResponse = httpclient.GetStringAsync(new Uri(mockInitContract)).Result; System.Diagnostics.Debug.WriteLine(mockInitResponse); } } From fefe815ae5feb0becb31ed8a72f63768ea89db13 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Tue, 23 May 2023 22:23:51 +0530 Subject: [PATCH 33/71] refactor around dispatcher --- .../EndPoint/PubSub/SubscribeOperation2.cs | 152 +++--------------- .../PubnubApi/EventEngine/EffectDispatcher.cs | 43 +++-- src/Api/PubnubApi/EventEngine/EventEngine.cs | 139 ++++++++++++---- .../EventEngine/HandshakeEffectHandler.cs | 9 ++ .../HandshakeFailedEffectHandler.cs | 12 +- .../HandshakeReconnectEffectHandler.cs | 8 + .../EventEngine/IEffectInvocationHandler.cs | 1 + .../EventEngine/ReceivingEffectHandler.cs | 34 ++++ .../EventEngine/ReconnectingEffectHandler.cs | 8 + 9 files changed, 222 insertions(+), 184 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 25b6da827..ff197b534 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -44,156 +44,50 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j pubnubTelemetryMgr = telemetryManager; pubnubTokenMgr = tokenManager; - - var effectDispatcher = new EffectDispatcher(); - effectDispatcher.PubnubUnitTest = unit; - var eventEmitter = new EventEmitter(); eventEmitter.RegisterJsonListener(JsonCallback); - pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); - pnEventEngine.PubnubUnitTest = unit; - pnEventEngine.Setup(config); - - var handshakeEffectHandler = new HandshakeEffectHandler(eventEmitter); handshakeEffectHandler.LogCallback = LogCallback; handshakeEffectHandler.HandshakeRequested += HandshakeEffect_HandshakeRequested; - #region Handshake state invocation and emit status - State handshakingState = pnEventEngine.States - .First(s => s.StateType == StateType.Handshaking); - foreach(var effectInvocation in handshakingState.EffectInvocationsList[EventType.HandshakeSuccess]) - { - if (effectInvocation is EmitStatus) - { - ((EmitStatus)effectInvocation).AnnounceStatus = Announce; - ((EmitStatus)effectInvocation).Handler = handshakeEffectHandler; - } - } - foreach(var effectInvocation in handshakingState.EntryList) - { - if (effectInvocation is Handshake) - { - ((Handshake)effectInvocation).Handler = handshakeEffectHandler; - } - } - foreach(var effectInvocation in handshakingState.ExitList) - { - if (effectInvocation is CancelHandshake) - { - ((CancelHandshake)effectInvocation).Handler = handshakeEffectHandler; - } - } - #endregion + handshakeEffectHandler.AnnounceStatus = Announce; var handshakeReconnectEffectHandler = new HandshakeReconnectEffectHandler(eventEmitter); handshakeReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; handshakeReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; handshakeReconnectEffectHandler.LogCallback = LogCallback; handshakeReconnectEffectHandler.HandshakeReconnectRequested += HandshakeReconnectEffect_HandshakeRequested; - #region HandshakeReconnecting state invocation and emit status - State handshakeReconnectingState = pnEventEngine.States - .First(s => s.StateType == StateType.HandshakeReconnecting); - foreach(var effectInvocation in handshakeReconnectingState.EffectInvocationsList[EventType.HandshakeReconnectSuccess]) - { - if (effectInvocation is EmitStatus) - { - ((EmitStatus)effectInvocation).AnnounceStatus = Announce; - ((EmitStatus)effectInvocation).Handler = handshakeReconnectEffectHandler; - } - } - //TBD - Do we need to emit status/error on HandshakeReconnectFailure - //foreach(var effectInvocation in handshakeReconnectingState.EffectInvocationsList[EventType.HandshakeReconnectFailure]) - //{ - //if (effectInvocation is EmitStatus) - //{ - // ((EmitStatus)effectInvocation).AnnounceStatus = Announce; - // ((EmitStatus)effectInvocation).Handler = handshakeReconnectEffectHandler; - //} - //} - foreach(var effectInvocation in handshakeReconnectingState.EffectInvocationsList[EventType.HandshakeReconnectGiveUp]) - { - if (effectInvocation is EmitStatus) - { - ((EmitStatus)effectInvocation).AnnounceStatus = Announce; - ((EmitStatus)effectInvocation).Handler = handshakeReconnectEffectHandler; - } - } - foreach(var effectInvocation in handshakeReconnectingState.EntryList) - { - if (effectInvocation is HandshakeReconnect) - { - ((HandshakeReconnect)effectInvocation).Handler = handshakeReconnectEffectHandler; - } - } - foreach(var effectInvocation in handshakeReconnectingState.ExitList) - { - if (effectInvocation is CancelHandshakeReconnect) - { - ((CancelHandshakeReconnect)effectInvocation).Handler = handshakeReconnectEffectHandler; - } - } - #endregion + handshakeReconnectEffectHandler.AnnounceStatus = Announce; var handshakeFailedEffectHandler = new HandshakeFailedEffectHandler(eventEmitter); handshakeFailedEffectHandler.LogCallback = LogCallback; - #region HandshakeFailed state invocation and emit status - State handshakeFailedState = pnEventEngine.States - .First(s => s.StateType == StateType.HandshakeFailed); - foreach(var effectInvocation in handshakeFailedState.EntryList) - { - if (effectInvocation is HandshakeFailed) - { - ((HandshakeFailed)effectInvocation).Handler = handshakeFailedEffectHandler; - } - } - foreach(var effectInvocation in handshakeFailedState.ExitList) - { - if (effectInvocation is CancelHandshakeFailed) - { - ((CancelHandshakeFailed)effectInvocation).Handler = handshakeFailedEffectHandler; - } - } - #endregion var receivingEffectHandler = new ReceivingEffectHandler(eventEmitter); receivingEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; receivingEffectHandler.LogCallback = LogCallback; receivingEffectHandler.ReceiveRequested += ReceivingEffect_ReceiveRequested; - #region Receiving state invocation and emit status - State receivingState = pnEventEngine.States - .First(s => s.StateType == StateType.Receiving); - foreach(var effectInvocation in receivingState.EffectInvocationsList[EventType.ReceiveSuccess]) - { - if (effectInvocation is EmitStatus) - { - ((EmitStatus)effectInvocation).AnnounceStatus = Announce; - ((EmitStatus)effectInvocation).Handler = receivingEffectHandler; - } - if (effectInvocation is EmitMessages) - { - //((EmitMessages)effectInvocation).SubscribeOperation = this; - ((EmitMessages)effectInvocation).AnnounceMessage = Announce; - ((EmitMessages)effectInvocation).LogCallback = LogCallback; - ((EmitMessages)effectInvocation).Handler = receivingEffectHandler; - } - } - foreach(var effectInvocation in receivingState.EntryList) - { - if (effectInvocation is ReceiveMessages) - { - ((ReceiveMessages)effectInvocation).Handler = receivingEffectHandler; - } - } - foreach(var effectInvocation in receivingState.ExitList) - { - if (effectInvocation is CancelReceiveMessages) - { - ((CancelReceiveMessages)effectInvocation).Handler = receivingEffectHandler; - } - } - #endregion + receivingEffectHandler.AnnounceStatus = Announce; + receivingEffectHandler.AnnounceMessage = Announce; + var effectDispatcher = new EffectDispatcher(); + effectDispatcher.PubnubUnitTest = unit; + effectDispatcher.Register(EventType.Handshake,handshakeEffectHandler); + effectDispatcher.Register(EventType.CancelHandshake,handshakeEffectHandler); + effectDispatcher.Register(EventType.HandshakeSuccess, handshakeEffectHandler); + + effectDispatcher.Register(EventType.HandshakeFailure, handshakeFailedEffectHandler); + effectDispatcher.Register(EventType.CancelHandshakeFailure, handshakeFailedEffectHandler); + + effectDispatcher.Register(EventType.HandshakeReconnectSuccess, handshakeReconnectEffectHandler); + effectDispatcher.Register(EventType.HandshakeReconnectGiveUp, handshakeReconnectEffectHandler); + + effectDispatcher.Register(EventType.ReceiveMessages, receivingEffectHandler); + effectDispatcher.Register(EventType.CancelReceiveMessages, receivingEffectHandler); + effectDispatcher.Register(EventType.ReceiveSuccess, receivingEffectHandler); + + pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); + pnEventEngine.PubnubUnitTest = unit; + pnEventEngine.Setup(config); if (pnEventEngine.PubnubUnitTest != null) { diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs index 7c6801f39..b95453890 100644 --- a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -14,16 +14,16 @@ public enum DispatcherType } public IPubnubUnitTest PubnubUnitTest { get; set; } - public Dictionary effectActionMap; + private Dictionary effectInvocationActionMap; public EffectDispatcher() { - effectActionMap = new Dictionary(); + effectInvocationActionMap = new Dictionary(); } public async void dispatch(EventType effect, ExtendedState stateContext) { IEffectInvocationHandler? handler; - if (effectActionMap.TryGetValue(effect, out handler)) { + if (effectInvocationActionMap.TryGetValue(effect, out handler)) { if (handler != null) { await Task.Factory.StartNew(()=> handler.Start(stateContext, effect)).ConfigureAwait(false); @@ -31,33 +31,26 @@ public async void dispatch(EventType effect, ExtendedState stateContext) } } - public async void dispatch(DispatcherType dispatchType, EventType eventType,List effectInvocations, ExtendedState stateContext) + public async void dispatch(EventType eventType,List effectInvocations, ExtendedState stateContext) { if (effectInvocations == null || effectInvocations.Count == 0) { return; } - foreach (var effect in effectInvocations) { - PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", effect.Name)); - System.Diagnostics.Debug.WriteLine("Found effect " + effect.Effectype); - if (dispatchType == DispatcherType.Exit) - { - await Task.Factory.StartNew(()=> effect.Handler?.Cancel()).ConfigureAwait(false); - } - else if (dispatchType == DispatcherType.Entry) - { - await Task.Factory.StartNew(()=> effect.Handler?.Start(stateContext, eventType)).ConfigureAwait(false); - } - else if (dispatchType == DispatcherType.Managed) + 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 (effect is EmitStatus) + if (invocation.IsManaged()) { - ((EmitStatus)effect).Announce(); + await Task.Factory.StartNew(()=> currentEffectInvocationhandler?.Start(stateContext, eventType)).ConfigureAwait(false); } - else if (effect is EmitMessages) + else if (invocation.IsCancelling()) { - ((EmitMessages)effect).Announce(); + await Task.Factory.StartNew(()=> currentEffectInvocationhandler?.Cancel()).ConfigureAwait(false); } - else if (effect is EmitMessages) + else { - ((EmitMessages)effect).Announce(); + currentEffectInvocationhandler.Run(stateContext); } } } @@ -66,7 +59,11 @@ public async void dispatch(DispatcherType dispatchType, EventType eventType,List public void Register(EventType type, IEffectInvocationHandler handler) { - effectActionMap.Add(type, handler); + if (effectInvocationActionMap.ContainsKey(type)) + { + throw new ArgumentException("EventType already exist"); + } + effectInvocationActionMap.Add(type, handler); } } } diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 8b9cfb924..3b3b93ac7 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -121,17 +121,37 @@ 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 { @@ -140,19 +160,49 @@ public class ReceiveReconnect: EffectInvocation 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 { @@ -161,10 +211,25 @@ public class HandshakeReconnect : EffectInvocation public SubscriptionCursor SubscriptionCursor { get; set; } public int Attempts { get; set; } public PubnubError Reason { get; set; } + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } } public class CancelHandshakeReconnect : EffectInvocation { - + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return true; + } } public class EmitStatus : EffectInvocation { @@ -195,6 +260,15 @@ public void Announce() } } } + + public override bool IsManaged() + { + return false; + } + public override bool IsCancelling() + { + return false; + } } public class EmitMessages : EffectInvocation { @@ -207,38 +281,41 @@ public EmitMessages(List messages) } public void Announce() { - Message[] receiveMessages = ((ReceivingEffectHandler)Handler).GetMessages(); - int messageCount = receiveMessages.Length; - if (receiveMessages != null && receiveMessages.Length > 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) - { - var presenceData = JsonConvert.DeserializeObject(receiveMessages[index].Payload.ToString()); - } - else - { - 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 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 @@ -338,11 +415,11 @@ public void Transition(Event e) PubnubUnitTest.EventTypeList.Add(new KeyValuePair("event", e.Name)); PubnubUnitTest.Attempts = e.Attempts; } - if (findState != null ) + if (findState != null) { if (findState.ExitList != null && findState.ExitList.Count > 0) { - Dispatcher.dispatch(EffectDispatcher.DispatcherType.Exit, e.EventType, findState.ExitList, this.Context); + Dispatcher.dispatch(e.EventType, findState.ExitList, this.Context); } if (findState.EffectInvocationsList != null && findState.EffectInvocationsList.ContainsKey(e.EventType) @@ -351,7 +428,7 @@ public void Transition(Event e) List effectInvocationList = findState.EffectInvocationsList[e.EventType]; if (effectInvocationList != null && effectInvocationList.Count > 0) { - Dispatcher.dispatch(EffectDispatcher.DispatcherType.Managed, e.EventType, effectInvocationList, this.Context); + Dispatcher.dispatch(e.EventType, effectInvocationList, this.Context); } } @@ -359,7 +436,7 @@ public void Transition(Event e) UpdateContext(e.EventType, e.EventPayload); if (CurrentState.EntryList != null && CurrentState.EntryList.Count > 0) { - Dispatcher.dispatch(EffectDispatcher.DispatcherType.Entry, e.EventType, CurrentState.EntryList, this.Context); + Dispatcher.dispatch(e.EventType, CurrentState.EntryList, this.Context); } UpdateContext(e.EventType, e.EventPayload); diff --git a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs index 605cf813e..33df62c92 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs @@ -45,6 +45,7 @@ public class HandshakeEffectHandler : IEffectInvocationHandler //public EffectInvocationType InvocationType { get; set; } private ExtendedState extendedState { get; set;} public Action LogCallback { get; set; } + public Action AnnounceStatus { get; set; } private PNStatus pnStatus { get; set; } public event EventHandler? HandshakeRequested; @@ -150,11 +151,19 @@ public void Cancel() cancellationTokenSource.Cancel(); } } + 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 index 89a63d004..2c6243689 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using System.Threading; +using System.Net.NetworkInformation; namespace PubnubApi.PubnubEventEngine { @@ -12,7 +13,9 @@ 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; @@ -50,10 +53,17 @@ public void Cancel() cancellationTokenSource.Cancel(); } } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + } public PNStatus GetPNStatus() { - throw new NotImplementedException(); + return pnStatus; } } } diff --git a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs index cc9e5344a..b0f2cf464 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs @@ -22,6 +22,7 @@ public class HandshakeReconnectEffectHandler : IEffectInvocationHandler //public EffectInvocationType InvocationType { get; set; } 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; } @@ -242,6 +243,13 @@ public void Cancel() cancellationTokenSource.Cancel(); } } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + } public PNStatus GetPNStatus() { diff --git a/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs b/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs index c974ab754..4350f3a22 100644 --- a/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs +++ b/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs @@ -26,6 +26,7 @@ public interface IEffectInvocationHandler { void Start(ExtendedState context, EventType eventType); void Cancel(); + void Run(ExtendedState context); PNStatus GetPNStatus(); } diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs index f11ce88ce..7e7943539 100644 --- a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; @@ -51,6 +52,8 @@ public class ReceivingEffectHandler : IEffectInvocationHandler, IReceiveMessa 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 PNReconnectionPolicy ReconnectionPolicy { get; set; } public event EventHandler? ReceiveRequested; @@ -162,6 +165,37 @@ public void Cancel() cancellationTokenSource.Cancel(); } } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + if (AnnounceMessage != null) + { + Message[] receiveMessages = GetMessages(); + int messageCount = receiveMessages.Length; + if (receiveMessages != null && receiveMessages.Length > 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) + { + var presenceData = JsonConvert.DeserializeObject(receiveMessages[index].Payload.ToString()); + } + else + { + 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() { diff --git a/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs index 3c46a42a1..6941e5d58 100644 --- a/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs @@ -6,6 +6,7 @@ public class ReconnectingEffectHandler : IEffectInvocationHandler { EventEmitter eventEmitter; private PNStatus pnStatus { get; set; } + public Action AnnounceStatus { get; set; } public ReconnectingEffectHandler(EventEmitter emitter) { this.eventEmitter = emitter; @@ -25,6 +26,13 @@ public void Cancel() { System.Diagnostics.Debug.WriteLine("ReconnectingEffectHandler Cancelled!!!"); } + public void Run(ExtendedState context) + { + if (AnnounceStatus != null) + { + AnnounceStatus(pnStatus); + } + } public PNStatus GetPNStatus() { return pnStatus; From f76cd7990b822799f67c9d2f97cf745bd642d0a8 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Tue, 23 May 2023 22:32:13 +0530 Subject: [PATCH 34/71] unwanted namespace --- src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs index 2c6243689..52100ca4a 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs @@ -4,7 +4,6 @@ using System.Text; using System.Threading.Tasks; using System.Threading; -using System.Net.NetworkInformation; namespace PubnubApi.PubnubEventEngine { From 00d9b9fa0e2eaf4801490c56669ab46ee1943df9 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Tue, 23 May 2023 23:44:57 +0530 Subject: [PATCH 35/71] fix HandshakeReconnect IsManaged flag --- src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs | 2 ++ src/Api/PubnubApi/EventEngine/EventEngine.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index ff197b534..64e520795 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -78,6 +78,8 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j effectDispatcher.Register(EventType.HandshakeFailure, handshakeFailedEffectHandler); effectDispatcher.Register(EventType.CancelHandshakeFailure, handshakeFailedEffectHandler); + effectDispatcher.Register(EventType.HandshakeReconnect, handshakeReconnectEffectHandler); + effectDispatcher.Register(EventType.CancelHandshakeReconnect, handshakeReconnectEffectHandler); effectDispatcher.Register(EventType.HandshakeReconnectSuccess, handshakeReconnectEffectHandler); effectDispatcher.Register(EventType.HandshakeReconnectGiveUp, handshakeReconnectEffectHandler); diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 3b3b93ac7..1edc9ada2 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -213,11 +213,11 @@ public class HandshakeReconnect : EffectInvocation public PubnubError Reason { get; set; } public override bool IsManaged() { - return false; + return true; } public override bool IsCancelling() { - return true; + return false; } } public class CancelHandshakeReconnect : EffectInvocation From 09df4f3c14b26aeedf0563adfd5532b18fe411df Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 29 May 2023 15:11:31 +0530 Subject: [PATCH 36/71] not required code --- src/Api/PubnubApi/EventEngine/EffectDispatcher.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs index b95453890..13fa9ca34 100644 --- a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -6,12 +6,6 @@ namespace PubnubApi.PubnubEventEngine { public class EffectDispatcher { - public enum DispatcherType - { - Entry, - Exit, - Managed - } public IPubnubUnitTest PubnubUnitTest { get; set; } private Dictionary effectInvocationActionMap; From a437f2bf8d3d430e056581127f284ff0f7ab9c6c Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 29 May 2023 15:53:05 +0530 Subject: [PATCH 37/71] removed unused dispatch method --- src/Api/PubnubApi/EventEngine/EffectDispatcher.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs index 13fa9ca34..0f94f3a99 100644 --- a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs @@ -14,17 +14,6 @@ public EffectDispatcher() effectInvocationActionMap = new Dictionary(); } - public async void dispatch(EventType effect, ExtendedState stateContext) - { - IEffectInvocationHandler? handler; - if (effectInvocationActionMap.TryGetValue(effect, out handler)) { - if (handler != null) - { - await Task.Factory.StartNew(()=> handler.Start(stateContext, effect)).ConfigureAwait(false); - } - } - } - public async void dispatch(EventType eventType,List effectInvocations, ExtendedState stateContext) { if (effectInvocations == null || effectInvocations.Count == 0) { return; } From 58a295ea2d18d320e073ea1789ff9382920f48fe Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Thu, 1 Jun 2023 14:05:14 +0530 Subject: [PATCH 38/71] CancelHandshake --- .../EndPoint/PubSub/SubscribeOperation2.cs | 5 ++++ .../EventEngine/HandshakeEffectHandler.cs | 26 +++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 64e520795..52e8b2b42 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -50,6 +50,7 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j var handshakeEffectHandler = new HandshakeEffectHandler(eventEmitter); handshakeEffectHandler.LogCallback = LogCallback; handshakeEffectHandler.HandshakeRequested += HandshakeEffect_HandshakeRequested; + handshakeEffectHandler.CancelHandshakeRequested += HandshakeEffect_CancelHandshakeRequested; handshakeEffectHandler.AnnounceStatus = Announce; var handshakeReconnectEffectHandler = new HandshakeReconnectEffectHandler(eventEmitter); @@ -124,6 +125,10 @@ private void HandshakeReconnectEffect_HandshakeRequested(object sender, Handshak string jsonResp = resp.Item1; e.HandshakeReconnectResponseCallback?.Invoke(jsonResp); } + private void HandshakeEffect_CancelHandshakeRequested(object sender, CancelHandshakeRequestEventArgs e) + { + manager.HandshakeRequestCancellation(); + } private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) { diff --git a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs index 33df62c92..aa246b544 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs @@ -9,10 +9,10 @@ namespace PubnubApi.PubnubEventEngine public class HandshakeResponse { [JsonProperty("t")] - public Timetoken? Timetoken { get; set; } + public Timetoken Timetoken { get; set; } [JsonProperty("m")] - public object[]? Messages { get; set; } + public object[] Messages { get; set; } } public class HandshakeError { @@ -38,27 +38,38 @@ public class HandshakeRequestEventArgs : EventArgs public ExtendedState ExtendedState { get; set; } public Action HandshakeResponseCallback { get; set; } } + public class CancelHandshakeRequestEventArgs : EventArgs + { + } public class HandshakeEffectHandler : IEffectInvocationHandler { EventEmitter emitter; - //public EffectInvocationType InvocationType { get; set; } 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; + 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; + CancellationTokenSource cancellationTokenSource; public HandshakeEffectHandler(EventEmitter emitter) { this.emitter = emitter; @@ -150,6 +161,11 @@ public void Cancel() LogCallback?.Invoke($"HandshakeEffectHandler - cancellationTokenSource - cancellion attempted."); cancellationTokenSource.Cancel(); } + + LogCallback?.Invoke($"HandshakeEffectHandler - invoking OnCancelHandshakeRequested."); + CancelHandshakeRequestEventArgs args = new CancelHandshakeRequestEventArgs(); + OnCancelHandshakeRequested(args); + } public void Run(ExtendedState context) { From 189b2c420ef7ab67fd3b19546420a5bcb45fc294 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Fri, 2 Jun 2023 19:50:13 +0530 Subject: [PATCH 39/71] CancelReceive and CancelHandshakeReconnect --- .../EndPoint/PubSub/SubscribeOperation2.cs | 10 +++++++ .../EventEngine/HandshakeEffectHandler.cs | 5 ++-- .../HandshakeReconnectEffectHandler.cs | 22 ++++++++++++--- .../EventEngine/ReceivingEffectHandler.cs | 27 ++++++++++++++++--- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 52e8b2b42..1ca56505c 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -58,6 +58,7 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j handshakeReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; handshakeReconnectEffectHandler.LogCallback = LogCallback; handshakeReconnectEffectHandler.HandshakeReconnectRequested += HandshakeReconnectEffect_HandshakeRequested; + handshakeReconnectEffectHandler.CancelHandshakeReconnectRequested += HandshakeReconnectEffect_CancelHandshakeRequested; handshakeReconnectEffectHandler.AnnounceStatus = Announce; var handshakeFailedEffectHandler = new HandshakeFailedEffectHandler(eventEmitter); @@ -67,6 +68,7 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j receivingEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; receivingEffectHandler.LogCallback = LogCallback; receivingEffectHandler.ReceiveRequested += ReceivingEffect_ReceiveRequested; + receivingEffectHandler.CancelReceiveRequested += ReceivingEffect_CancelReceiveRequested; receivingEffectHandler.AnnounceStatus = Announce; receivingEffectHandler.AnnounceMessage = Announce; @@ -129,6 +131,14 @@ private void HandshakeEffect_CancelHandshakeRequested(object sender, CancelHands { manager.HandshakeRequestCancellation(); } + private void HandshakeReconnectEffect_CancelHandshakeRequested(object sender, CancelHandshakeReconnectRequestEventArgs e) + { + manager.HandshakeRequestCancellation(); + } + private void ReceivingEffect_CancelReceiveRequested(object sender, CancelReceiveRequestEventArgs e) + { + manager.ReceiveRequestCancellation(); + } private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) { diff --git a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs index aa246b544..a93d2326e 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs @@ -50,8 +50,8 @@ public class HandshakeEffectHandler : IEffectInvocationHandler public Action AnnounceStatus { get; set; } private PNStatus pnStatus { get; set; } - public event EventHandler? HandshakeRequested; - public event EventHandler? CancelHandshakeRequested; + public event EventHandler HandshakeRequested; + public event EventHandler CancelHandshakeRequested; protected virtual void OnHandshakeRequested(HandshakeRequestEventArgs e) { EventHandler handler = HandshakeRequested; @@ -165,7 +165,6 @@ public void Cancel() LogCallback?.Invoke($"HandshakeEffectHandler - invoking OnCancelHandshakeRequested."); CancelHandshakeRequestEventArgs args = new CancelHandshakeRequestEventArgs(); OnCancelHandshakeRequested(args); - } public void Run(ExtendedState context) { diff --git a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs index b0f2cf464..2b6b2d8b9 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs @@ -16,10 +16,12 @@ public class HandshakeReconnectRequestEventArgs : EventArgs public ExtendedState ExtendedState { get; set; } public Action HandshakeReconnectResponseCallback { get; set; } } + public class CancelHandshakeReconnectRequestEventArgs : EventArgs + { + } public class HandshakeReconnectEffectHandler : IEffectInvocationHandler { EventEmitter emitter; - //public EffectInvocationType InvocationType { get; set; } private ExtendedState extendedState { get; set;} public Action LogCallback { get; set; } public Action AnnounceStatus { get; set; } @@ -31,18 +33,27 @@ public class HandshakeReconnectEffectHandler : IEffectInvocationHandler const int MAXEXPONENTIALBACKOFF = 25; const int INTERVAL = 3; - public event EventHandler? HandshakeReconnectRequested; + public event EventHandler HandshakeReconnectRequested; + public event EventHandler CancelHandshakeReconnectRequested; System.Threading.Timer timer; protected virtual void OnHandshakeReconnectRequested(HandshakeReconnectRequestEventArgs e) { - EventHandler? handler = HandshakeReconnectRequested; + 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; + CancellationTokenSource cancellationTokenSource; public HandshakeReconnectEffectHandler(EventEmitter emitter) { this.emitter = emitter; @@ -242,6 +253,9 @@ public void Cancel() LogCallback?.Invoke($"HandshakeReconnectEffectHandler - cancellationTokenSource - cancellion attempted."); cancellationTokenSource.Cancel(); } + LogCallback?.Invoke($"HandshakeReconnectEffectHandler - invoking OnCancelHandshakeReconnectRequested."); + CancelHandshakeReconnectRequestEventArgs args = new CancelHandshakeReconnectRequestEventArgs(); + OnCancelHandshakeReconnectRequested(args); } public void Run(ExtendedState context) { diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs index 7e7943539..1a171acd6 100644 --- a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -9,7 +9,7 @@ namespace PubnubApi.PubnubEventEngine public class ReceiveingResponse { [JsonProperty("t")] - public Timetoken? Timetoken { get; set; } + public Timetoken Timetoken { get; set; } [JsonProperty("m")] public Message[] Messages { get; set; } @@ -45,6 +45,12 @@ 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; @@ -56,17 +62,27 @@ public class ReceivingEffectHandler : IEffectInvocationHandler, IReceiveMessa public Action> AnnounceMessage { get; set; } public PNReconnectionPolicy ReconnectionPolicy { get; set; } - public event EventHandler? ReceiveRequested; + public event EventHandler ReceiveRequested; + public event EventHandler CancelReceiveRequested; protected virtual void OnReceiveRequested(ReceiveRequestEventArgs e) { - EventHandler? handler = ReceiveRequested; + 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; + CancellationTokenSource cancellationTokenSource; public ReceivingEffectHandler(EventEmitter emitter) { @@ -164,6 +180,9 @@ public void Cancel() 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) { From f6550091c02ad260b260396f0271e96c62733ce0 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 5 Jun 2023 23:39:53 +0530 Subject: [PATCH 40/71] ReceivingReconnect WIP --- .../EndPoint/PubSub/SubscribeOperation2.cs | 24 ++ src/Api/PubnubApi/EventEngine/EventEngine.cs | 21 +- .../HandshakeReconnectEffectHandler.cs | 1 + .../ReceiveReconnectingEffectHandler.cs | 268 ++++++++++++++++++ .../EventEngine/ReceivingEffectHandler.cs | 25 +- .../EventEngine/ReconnectingEffectHandler.cs | 41 --- src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 2 +- src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 2 +- 8 files changed, 323 insertions(+), 61 deletions(-) create mode 100644 src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs delete mode 100644 src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 1ca56505c..a316d388e 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -72,6 +72,14 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j receivingEffectHandler.AnnounceStatus = Announce; receivingEffectHandler.AnnounceMessage = Announce; + var receiveReconnectEffectHandler = new ReceiveReconnectingEffectHandler(eventEmitter); + receiveReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; + receiveReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; + receiveReconnectEffectHandler.LogCallback = LogCallback; + receiveReconnectEffectHandler.ReceiveReconnectRequested += ReceiveReconnectEffect_ReceiveRequested; + receiveReconnectEffectHandler.CancelReceiveReconnectRequested += ReceiveReconnectEffect_CancelReceiveRequested; + receiveReconnectEffectHandler.AnnounceStatus = Announce; + var effectDispatcher = new EffectDispatcher(); effectDispatcher.PubnubUnitTest = unit; effectDispatcher.Register(EventType.Handshake,handshakeEffectHandler); @@ -90,6 +98,11 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j effectDispatcher.Register(EventType.CancelReceiveMessages, receivingEffectHandler); effectDispatcher.Register(EventType.ReceiveSuccess, receivingEffectHandler); + effectDispatcher.Register(EventType.ReceiveReconnect, receiveReconnectEffectHandler); + effectDispatcher.Register(EventType.CancelReceiveReconnect, receiveReconnectEffectHandler); + effectDispatcher.Register(EventType.ReceiveReconnectSuccess, receiveReconnectEffectHandler); + effectDispatcher.Register(EventType.ReceiveReconnectGiveUp, receiveReconnectEffectHandler); + pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); pnEventEngine.PubnubUnitTest = unit; pnEventEngine.Setup(config); @@ -139,6 +152,17 @@ private void ReceivingEffect_CancelReceiveRequested(object sender, CancelReceive { manager.ReceiveRequestCancellation(); } + private void ReceiveReconnectEffect_ReceiveRequested(object sender, ReceiveReconnectRequestEventArgs e) + { + Tuple resp = manager.ReceiveRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; + + string jsonResp = resp.Item1; + e.ReceiveReconnectResponseCallback?.Invoke(jsonResp); + } + private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, CancelReceiveReconnectRequestEventArgs e) + { + manager.ReceiveRequestCancellation(); + } private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) { diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 1edc9ada2..132eea9c8 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -255,6 +255,11 @@ public void Announce() //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); } @@ -642,7 +647,7 @@ public void Setup(PNConfiguration config) EmitMessages receiveEmitMessages = new EmitMessages(null); receiveEmitMessages.Name = "EMIT_EVENTS"; - receiveEmitMessages.Effectype = EventType.ReceiveSuccess; + receiveEmitMessages.Effectype = EventType.ReceiveMessages; EmitStatus receiveDisconnectEmitStatus = new EmitStatus(); receiveDisconnectEmitStatus.Name = "EMIT_STATUS"; @@ -662,7 +667,7 @@ public void Setup(PNConfiguration config) .On(EventType.SubscriptionRestored, StateType.Receiving) .On(EventType.ReceiveSuccess, StateType.Receiving, new List() { - receiveEmitStatus, + //receiveEmitStatus, receiveEmitMessages } ) @@ -696,11 +701,11 @@ public void Setup(PNConfiguration config) #region ReceiveReconnecting Effect Invocations and Emit Status EmitStatus receiveReconnectEmitStatus = new EmitStatus(); receiveReconnectEmitStatus.Name = "RECONNECT_EMIT_STATUS"; - receiveEmitStatus.Effectype = EventType.ReceiveReconnectSuccess; + receiveReconnectEmitStatus.Effectype = EventType.ReceiveReconnectSuccess; EmitMessages receiveReconnectEmitMessages = new EmitMessages(null); receiveReconnectEmitMessages.Name = "RECEIVE_RECONNECT_EVENTS"; - receiveReconnectEmitMessages.Effectype = EventType.ReceiveReconnectSuccess; + receiveReconnectEmitMessages.Effectype = EventType.ReceiveMessages; EmitStatus receiveReconnectDisconnectEmitStatus = new EmitStatus(); receiveReconnectDisconnectEmitStatus.Name = "RECONNECT_DISCONNECT_STATUS"; @@ -724,15 +729,11 @@ public void Setup(PNConfiguration config) .On(EventType.SubscriptionRestored, StateType.Receiving) .On(EventType.ReceiveReconnectSuccess, StateType.Receiving, new List() { - receiveReconnectEmitStatus, + //receiveReconnectEmitStatus, receiveReconnectEmitMessages } ) - .On(EventType.Disconnect, StateType.ReceiveStopped, new List() - { - receiveReconnectDisconnectEmitStatus - } - ) + .On(EventType.Disconnect, StateType.ReceiveStopped) .On(EventType.ReceiveReconnectGiveUp, StateType.ReceiveFailed, new List() { receiveReconnectGiveupEmitStatus diff --git a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs index 2b6b2d8b9..e8fa53f5c 100644 --- a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs @@ -233,6 +233,7 @@ private void PrepareAndEmitHandshakeReconnectGiveupEvent(Exception ex) emitter.emit(handshakeReconnectGiveupEvent); } + //private void PrepareAndEmitHandshakeFailedEvent(Exception ex) //{ // HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); diff --git a/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs new file mode 100644 index 000000000..dac990de0 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs @@ -0,0 +1,268 @@ +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 + { + 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(); + } + 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 index 1a171acd6..599334105 100644 --- a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -23,6 +23,7 @@ public class Message [JsonProperty("d")] public T Payload { get; set; } } + public class PresenceEvent { [JsonProperty("action")] @@ -39,6 +40,14 @@ public class PresenceEvent } + public class ReceiveError + { + [JsonProperty("status")] + public int Status { get; set; } + + [JsonProperty("error")] + public string ErrorMessage { get; set; } + } public class ReceiveRequestEventArgs : EventArgs { @@ -125,13 +134,13 @@ public void OnReceivingEffectResponseReceived(string json) receiveSuccessEvent.Name = "RECEIVE_SUCCESS"; LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveSuccess"); - 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; + //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(receiveSuccessEvent); } @@ -186,7 +195,7 @@ public void Cancel() } public void Run(ExtendedState context) { - if (AnnounceStatus != null) + if (AnnounceStatus != null && pnStatus != null) { AnnounceStatus(pnStatus); } diff --git a/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs deleted file mode 100644 index 6941e5d58..000000000 --- a/src/Api/PubnubApi/EventEngine/ReconnectingEffectHandler.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; - -namespace PubnubApi.PubnubEventEngine -{ - public class ReconnectingEffectHandler : IEffectInvocationHandler - { - EventEmitter eventEmitter; - private PNStatus pnStatus { get; set; } - public Action AnnounceStatus { get; set; } - public ReconnectingEffectHandler(EventEmitter emitter) - { - this.eventEmitter = emitter; - } - - public void Start(ExtendedState context, EventType eventType) // TODO: Implementation of retry getDelay() as per policy - { - var evnt = new Reconnect(); - evnt.EventPayload.Timetoken = context.Timetoken; - evnt.EventPayload.Region = context.Region; - evnt.EventType = EventType.ReceiveSuccess; - evnt.Name = "RECEIVE_RECONNECT_SUCCESS"; - this.eventEmitter.emit(evnt); - } - - public void Cancel() - { - System.Diagnostics.Debug.WriteLine("ReconnectingEffectHandler Cancelled!!!"); - } - public void Run(ExtendedState context) - { - if (AnnounceStatus != null) - { - AnnounceStatus(pnStatus); - } - } - public PNStatus GetPNStatus() - { - return pnStatus; - } - } -} diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 559c1e5e2..066bf9e34 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -221,8 +221,8 @@ Addressed threading issue on reading ConcurrentDictionary keys. + - diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index db19d727a..609a09597 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -338,8 +338,8 @@ Addressed threading issue on reading ConcurrentDictionary keys. + - From 81e5f0e63e527cd8fe1c18e30927d28135e1895e Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 12 Jun 2023 13:56:57 +0530 Subject: [PATCH 41/71] ReceivingReconnect --- .../EndPoint/PubSub/SubscribeManager2.cs | 27 ++++++++++++++++++- .../EndPoint/PubSub/SubscribeOperation2.cs | 2 +- src/Api/PubnubApi/EventEngine/EventEngine.cs | 3 ++- .../ReceiveReconnectingEffectHandler.cs | 5 ++++ .../EventEngine/ReceivingEffectHandler.cs | 2 +- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs index fc56a81eb..56e2e6d96 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs @@ -205,7 +205,32 @@ internal void ReceiveRequestCancellation() } else { - LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => ReceiveRequestCancellation. No request to cancel.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); + 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); } } diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index a316d388e..cd9a24d2b 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -161,7 +161,7 @@ private void ReceiveReconnectEffect_ReceiveRequested(object sender, ReceiveRecon } private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, CancelReceiveReconnectRequestEventArgs e) { - manager.ReceiveRequestCancellation(); + manager.ReceiveReconnectRequestCancellation(); } private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 132eea9c8..37d23c842 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -720,7 +720,8 @@ public void Setup(PNConfiguration config) receiveReconnectInvocation.Effectype = EventType.ReceiveReconnect; EffectInvocation cancelReceiveReconnect = new CancelReceiveReconnect(); - cancelReceiveReconnect.Effectype = EventType.CancelReceiveMessages; + cancelReceiveReconnect.Name = "CANCEL_RECEIVE_RECONNECT"; + cancelReceiveReconnect.Effectype = EventType.CancelReceiveReconnect; #endregion #region StateType.ReceiveReconnecting CreateState(StateType.ReceiveReconnecting) diff --git a/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs index dac990de0..6e8872253 100644 --- a/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs @@ -115,6 +115,7 @@ 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) @@ -249,6 +250,10 @@ public void Cancel() 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); diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs index 599334105..ad9d900db 100644 --- a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; @@ -119,6 +118,7 @@ public void OnReceivingEffectResponseReceived(string json) { try { + pnStatus = null; var receivedResponse = JsonConvert.DeserializeObject>(json); if (receivedResponse != null && receivedResponse.Timetoken != null) { From 41fbe923be1798a8e269a5950cf2fc199002adc1 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Tue, 13 Jun 2023 10:35:20 +0530 Subject: [PATCH 42/71] PresenceEventResult --- .../EndPoint/PubSub/SubscribeOperation2.cs | 1 + .../EventEngine/ReceivingEffectHandler.cs | 104 ++++++++++++++---- 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index cd9a24d2b..7e9511bf7 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -71,6 +71,7 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j receivingEffectHandler.CancelReceiveRequested += ReceivingEffect_CancelReceiveRequested; receivingEffectHandler.AnnounceStatus = Announce; receivingEffectHandler.AnnounceMessage = Announce; + receivingEffectHandler.AnnouncePresenceEvent = Announce; var receiveReconnectEffectHandler = new ReceiveReconnectingEffectHandler(eventEmitter); receiveReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs index ad9d900db..1c719582c 100644 --- a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs @@ -16,11 +16,38 @@ public class ReceiveingResponse 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 @@ -68,6 +95,7 @@ public class ReceivingEffectHandler : IEffectInvocationHandler, IReceiveMessa 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; @@ -134,14 +162,6 @@ public void OnReceivingEffectResponseReceived(string json) receiveSuccessEvent.Name = "RECEIVE_SUCCESS"; LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveSuccess"); - //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(receiveSuccessEvent); } else @@ -178,8 +198,6 @@ public void OnReceivingEffectResponseReceived(string json) emitter.emit(receiveFailureEvent); } - //emitter.emit(evnt); - //emitter.emit(json, false, messageCount); } public void Cancel() @@ -199,26 +217,64 @@ public void Run(ExtendedState context) { AnnounceStatus(pnStatus); } - if (AnnounceMessage != null) - { - Message[] receiveMessages = GetMessages(); - int messageCount = receiveMessages.Length; - if (receiveMessages != null && receiveMessages.Length > 0) + Message[] receiveMessages = GetMessages(); + int messageCount = (receiveMessages != null) ? receiveMessages.Length : 0; + if (messageCount > 0) + { + for (int index = 0; index < receiveMessages.Length; index++) { - 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 { - LogCallback?.Invoke($"Received Message ({index + 1} of {receiveMessages.Length}) : {JsonConvert.SerializeObject(receiveMessages[index])}"); - if (receiveMessages[index].Channel.IndexOf("-pnpres") > 0) + 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) { - var presenceData = JsonConvert.DeserializeObject(receiveMessages[index].Payload.ToString()); + //TODO: Callback for File message } else { - 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); + //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); + } } } } From e1d65ca5df89ebb09f1fac4ff9b1bc9b0ed6c449 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Tue, 13 Jun 2023 12:05:13 +0530 Subject: [PATCH 43/71] Fix to pass contract test --- src/Api/PubnubApi/EventEngine/EventEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 37d23c842..1fa4c3e2a 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -667,7 +667,7 @@ public void Setup(PNConfiguration config) .On(EventType.SubscriptionRestored, StateType.Receiving) .On(EventType.ReceiveSuccess, StateType.Receiving, new List() { - //receiveEmitStatus, + receiveEmitStatus, receiveEmitMessages } ) From 84af23db29fe966587633792e3ecea1ca13b58c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= <106665140+MikeDobrzan@users.noreply.github.com> Date: Thu, 22 Jun 2023 12:57:26 +0200 Subject: [PATCH 44/71] Eventengine poc refactor (#170) * WIP EE refactor - core classes * WIP - reorganization * WIP - eventQueue * WIP state abstract class to interface --- .../EventEngine/Core/EffectDispatcher.cs | 39 +++++++++++ src/Api/PubnubApi/EventEngine/Core/Engine.cs | 55 +++++++++++++++ .../EventEngine/Core/EventEngineInterfaces.cs | 30 +++++++++ .../PubnubApi/EventEngine/Core/EventQueue.cs | 25 +++++++ src/Api/PubnubApi/EventEngine/EventEngine.cs | 2 + .../Effects/HandshakeEffectHandler.cs | 48 +++++++++++++ .../Subscribe/Events/SubscriptionEvents.cs | 67 +++++++++++++++++++ .../Invocations/SubscriptionInvocations.cs | 29 ++++++++ .../Subscribe/States/HandshakingState.cs | 17 +++++ .../Subscribe/States/SubscribedState.cs | 13 ++++ .../Subscribe/States/UnsubscribedState.cs | 31 +++++++++ .../Subscribe/SubscribeEventEngine.cs | 21 ++++++ 12 files changed, 377 insertions(+) create mode 100644 src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs create mode 100644 src/Api/PubnubApi/EventEngine/Core/Engine.cs create mode 100644 src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs create mode 100644 src/Api/PubnubApi/EventEngine/Core/EventQueue.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs new file mode 100644 index 000000000..babde75de --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace PubnubApi.PubnubEventEngine.Core { + internal class EffectDispatcher { + // assumes 1 instance of handler - capable of managing itself + private readonly Dictionary effectInvocationHandlerMap = + new Dictionary(); + + /// + /// Dispatch an invocation i.e. call a registered effect handler. + /// + public async Task Dispatch(T invocation) where T : IEffectInvocation { + if (!effectInvocationHandlerMap.ContainsKey(invocation.GetType())) { + throw new ArgumentException($"No handler for {invocation.GetType().Name} found."); + } + + if (invocation is IEffectCancelInvocation) { + await effectInvocationHandlerMap[invocation.GetType()].Cancel(); + } else { + await ((IEffectHandler)effectInvocationHandlerMap[invocation.GetType()]).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..1a26037d7 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace PubnubApi.PubnubEventEngine.Core { + internal abstract class Engine { + public EventQueue eventQueue = new EventQueue(); + + protected EffectDispatcher dispatcher = new EffectDispatcher(); + protected IState currentState; + + private Task currentTransition; + + public Engine() { + eventQueue.onEventQueued += OnEvent; + } + + ~Engine() { + eventQueue.onEventQueued -= OnEvent; + } + + private async void OnEvent(EventQueue q) { + if (!(currentTransition is null)) { + await currentTransition; + } + currentTransition = Transition(q.Dequeue()).ContinueWith((res) => currentState = res.Result); + } + + private async Task Transition(IEvent e) { + var stateInvocationPair = currentState.Transition(e); + + if (stateInvocationPair is null) { + return currentState; + } + + await ExecuteStateChange(currentState, stateInvocationPair.Item1, stateInvocationPair.Item2); + + return stateInvocationPair.Item1; + } + + /// + /// Launch the invocations associated with transitioning between states + /// + private async Task ExecuteStateChange(IState sourceState, IState targetState, IEnumerable invocations) { + foreach (var effectInvocation in sourceState.onExit) { + await dispatcher.Dispatch(effectInvocation); + } + foreach (var effectInvocation in invocations) { + await dispatcher.Dispatch(effectInvocation); + } + foreach (var effectInvocation in targetState.onEntry) { + 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..61d2ea495 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace PubnubApi.PubnubEventEngine.Core { + internal interface IEffectHandler { + Task Cancel(); + } + + internal interface IEffectHandler : IEffectHandler where T : IEffectInvocation { + Task Run(T invocation); + } + + internal interface IEffectInvocation { } + + internal interface IEffectCancelInvocation : IEffectInvocation { } + + internal interface IEvent { }; + + internal interface IState { + public abstract IEnumerable onEntry { get; } + public abstract IEnumerable onExit { get; } + + /// + /// The EE transition pure function. + /// + /// Input event + /// Target state and invocation list, or null for no-transition + public abstract System.Tuple> Transition(IEvent e); + } +} \ 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..1386d300c --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Linq; + +namespace PubnubApi.PubnubEventEngine.Core { + internal class EventQueue { + private volatile Queue eventQueue = new Queue(); + private object lockObj = new object(); + + public event System.Action onEventQueued; + + public void Enqueue(IEvent e) { + lock (lockObj) { + // TODO de-dupe? Throttle? + eventQueue.Enqueue(e); + onEventQueued?.Invoke(this); + } + } + + public IEvent Dequeue() { + lock (lockObj) { + return eventQueue.Any() ? eventQueue.Dequeue() : null; + } + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs index 1fa4c3e2a..5614914d9 100644 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/EventEngine.cs @@ -512,6 +512,8 @@ public State NextState() public void Setup(PNConfiguration config) { + + CreateState(StateType.Unsubscribed) .On(EventType.SubscriptionChanged, StateType.Handshaking) .On(EventType.SubscriptionRestored, StateType.Receiving); 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..f09621caa --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using PubnubApi.EndPoint; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.Effects { + internal class HandshakeEffectHandler : Core.IEffectHandler { + private SubscribeManager2 manager; + private EventQueue eventQueue; + + public HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { + this.manager = manager; + this.eventQueue = eventQueue; + } + + public async Task Run(HandshakeInvocation invocation) { + // TODO fix this, probably wrong :) + var resp = await manager.HandshakeRequest( + PNOperationType.PNSubscribeOperation, + invocation.channels.ToArray(), + invocation.channelGroups.ToArray(), + null, + null, + invocation.initialSubscribeQueryParams, + invocation.externalQueryParams + ); + + if (!resp.Item2.Error) { + // TODO move deserialization outside + // TODO does this need more error checking? + var handshakeResponse = JsonConvert.DeserializeObject(resp.Item1); + var c = new SubscriptionCursor() { + Region = handshakeResponse.Timetoken.Region, + Timetoken = handshakeResponse.Timetoken.Timestamp + }; + + eventQueue.Enqueue(new Events.HandshakeSuccessEvent() {cursor = c}); + } + } + + public async Task Cancel() { + manager.HandshakeRequestCancellation(); + } + } +} \ No newline at end of file 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..221fa68bb --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +namespace PubnubApi.PubnubEventEngine.Subscribe.Events { + 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 class HandshakeFailureEvent : Core.IEvent { + // TODO status or reason? + public PNStatus status; + } + + public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { + } + + public class HandshakeReconnectFailureEvent : HandshakeFailureEvent { + } + + public class HandshakeReconnectRetryEvent : Core.IEvent { + } + + public class HandshakeReconnectGiveUpEvent : Core.IEvent { + // TODO status or reason? + public PNStatus status; + } + + public class ReceiveSuccessEvent : Core.IEvent { + public List> messages; + public SubscriptionCursor cursor; + } + + public class ReceiveFailureEvent : Core.IEvent { + // TODO status or reason? + public PNStatus status; + } + + public class ReceiveReconnectRetry : Core.IEvent { + } + + public class ReceiveReconnectSuccessEvent : ReceiveSuccessEvent { + } + + public class ReceiveReconnectFailureEvent : ReceiveFailureEvent { + } + + public class ReceiveReconnectGiveUpEvent : Core.IEvent { + // TODO status or reason? + public PNStatus status; + } + + public class DisconnectEvent : Core.IEvent { + } + + public class ReconnectEvent : Core.IEvent { + } +} \ 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..2f33d4928 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; + +namespace PubnubApi.PubnubEventEngine.Subscribe.Invocations { + internal class EmitMessagesInvocation : Core.IEffectInvocation { + public List messages; + } + + internal class EmitStatusInvocation : Core.IEffectInvocation { + + } + + internal class HandshakeInvocation : Core.IEffectInvocation { + public IEnumerable channels; + public IEnumerable channelGroups; + public Dictionary initialSubscribeQueryParams = new Dictionary(); + public Dictionary externalQueryParams = new Dictionary(); + } + + internal class ReceiveMessagesInvocation : Core.IEffectInvocation { } + + internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } + + internal class HandshakeCancelInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } + + internal class ReconnectInvocation : Core.IEffectInvocation { } + + internal class CancelReconnectInvocation : ReconnectInvocation, Core.IEffectCancelInvocation { } +} \ 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..cae98bd0a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class HandshakingState : Core.IState { + + public IEnumerable channels; + public IEnumerable channelGroups; + + public IEnumerable onEntry { get; } + public IEnumerable onExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs new file mode 100644 index 000000000..d00659631 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class SubscribedState : Core.IState { + public IEnumerable onEntry { get; } + public IEnumerable onExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} \ 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..857375ceb --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class UnsubscribedState : Core.IState { + public IEnumerable onEntry { get; } + public IEnumerable onExit { get; } + + public Tuple> Transition(Core.IEvent e) { + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new HandshakingState() { + channels = subscriptionChanged.channels, + channelGroups = subscriptionChanged.channelGroups, + }, + new[] { + new HandshakeInvocation() { + channels = subscriptionChanged.channels, + channelGroups = subscriptionChanged.channelGroups, + }, + } + ); + + default: return 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..d0756455a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -0,0 +1,21 @@ +using PubnubApi.EndPoint; +using PubnubApi.PubnubEventEngine.Subscribe.Effects; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.PubnubEventEngine.Subscribe.States; + +namespace PubnubApi.PubnubEventEngine.Subscribe { + internal class SubscribeEventEngine : PubnubEventEngine.Core.Engine { + private SubscribeManager2 subscribeManager; + + public SubscribeEventEngine(SubscribeManager2 subscribeManager) { + this.subscribeManager = subscribeManager; + + // initialize the handler, pass dependencies + var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, eventQueue); + dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); + + currentState = new UnsubscribedState(); + } + } +} \ No newline at end of file From 5b8de6ce0f7048cef91b489f50a4f5ef8a615b2b Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Fri, 23 Jun 2023 14:14:41 +0530 Subject: [PATCH 45/71] added states and Pascal case property names (#171) * added states and Pascal case property names --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 4 +- .../EventEngine/Core/EventEngineInterfaces.cs | 4 +- .../Subscribe/Events/SubscriptionEvents.cs | 2 + .../Invocations/SubscriptionInvocations.cs | 8 +++- .../Subscribe/States/HandshakeFailedState.cs | 18 +++++++ .../States/HandshakeReconnectingState.cs | 47 +++++++++++++++++++ .../Subscribe/States/HandshakeStoppedState.cs | 18 +++++++ .../Subscribe/States/HandshakingState.cs | 4 +- .../Subscribe/States/ReceiveFailedState.cs | 19 ++++++++ .../States/ReceiveReconnectingState.cs | 20 ++++++++ .../Subscribe/States/ReceiveStoppedState.cs | 18 +++++++ .../Subscribe/States/ReceivingState.cs | 19 ++++++++ .../Subscribe/States/SubscribedState.cs | 4 +- .../Subscribe/States/UnsubscribedState.cs | 5 +- src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 24 +++++++++- src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 24 +++++++++- 16 files changed, 223 insertions(+), 15 deletions(-) create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 1a26037d7..d257ff3c8 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -41,13 +41,13 @@ private async Task Transition(IEvent e) { /// Launch the invocations associated with transitioning between states /// private async Task ExecuteStateChange(IState sourceState, IState targetState, IEnumerable invocations) { - foreach (var effectInvocation in sourceState.onExit) { + foreach (var effectInvocation in sourceState.OnExit) { await dispatcher.Dispatch(effectInvocation); } foreach (var effectInvocation in invocations) { await dispatcher.Dispatch(effectInvocation); } - foreach (var effectInvocation in targetState.onEntry) { + foreach (var effectInvocation in targetState.OnEntry) { await dispatcher.Dispatch(effectInvocation); } } diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 61d2ea495..201f027de 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -17,8 +17,8 @@ internal interface IEffectCancelInvocation : IEffectInvocation { } internal interface IEvent { }; internal interface IState { - public abstract IEnumerable onEntry { get; } - public abstract IEnumerable onExit { get; } + public abstract IEnumerable OnEntry { get; } + public abstract IEnumerable OnExit { get; } /// /// The EE transition pure function. diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index 221fa68bb..86295c0b6 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -60,6 +60,8 @@ public class ReceiveReconnectGiveUpEvent : Core.IEvent { } public class DisconnectEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; } public class ReconnectEvent : Core.IEvent { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs index 2f33d4928..cec835b12 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -23,7 +23,11 @@ internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core internal class HandshakeCancelInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } - internal class ReconnectInvocation : Core.IEffectInvocation { } + //internal class ReconnectInvocation : Core.IEffectInvocation { } + internal class HandshakeReconnectInvocation: Core.IEffectInvocation { } + internal class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } - internal class CancelReconnectInvocation : ReconnectInvocation, Core.IEffectCancelInvocation { } + internal class ReceiveReconnectInvocation: Core.IEffectInvocation { } + internal class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } + //internal class CancelReconnectInvocation : ReconnectInvocation, 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..1ed4edbec --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class HandshakeFailedState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} 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..dfe2561c1 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class HandshakeReconnectingState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new HandshakingState() { + channels = subscriptionChanged.channels, + channelGroups = subscriptionChanged.channelGroups, + }, + new[] { + new HandshakeInvocation() { + channels = subscriptionChanged.channels, + channelGroups = subscriptionChanged.channelGroups, + }, + } + ); + case Events.DisconnectEvent disconnectEvent: + return new Tuple>( + new HandshakeStoppedState() { + Channels = disconnectEvent.Channels, + ChannelGroups = disconnectEvent.ChannelGroups + }, + null + ); + case Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUpEvent: + return new Tuple>( + new HandshakeFailedState() { }, + null + ); + + default: return 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..9774bc6cb --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class HandshakeStoppedState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} \ 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 index cae98bd0a..29aa611b2 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -8,8 +8,8 @@ internal class HandshakingState : Core.IState { public IEnumerable channels; public IEnumerable channelGroups; - public IEnumerable onEntry { get; } - public IEnumerable onExit { get; } + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { throw new NotImplementedException(); } 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..41c4be2ee --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class ReceiveFailedState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} + 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..2a16d3c02 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class ReceiveReconnectingState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} + + 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..0d925e43e --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class ReceiveStoppedState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} 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..2208f539a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; + +namespace PubnubApi.PubnubEventEngine.Subscribe.States { + internal class ReceivingState : Core.IState { + + public IEnumerable Channels; + public IEnumerable ChannelGroups; + + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } + public Tuple> Transition(IEvent e) { + throw new NotImplementedException(); + } + } +} + diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs index d00659631..39de39a23 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs @@ -4,8 +4,8 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { internal class SubscribedState : Core.IState { - public IEnumerable onEntry { get; } - public IEnumerable onExit { get; } + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { throw new NotImplementedException(); } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 857375ceb..f8a45cd74 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -5,9 +5,8 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { internal class UnsubscribedState : Core.IState { - public IEnumerable onEntry { get; } - public IEnumerable onExit { get; } - + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } public Tuple> Transition(Core.IEvent e) { switch (e) { case Events.SubscriptionChangedEvent subscriptionChanged: diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 066bf9e34..fd3bba484 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -214,6 +214,10 @@ Addressed threading issue on reading ConcurrentDictionary keys. Enum\ResponseType.cs + + + + @@ -224,6 +228,20 @@ Addressed threading issue on reading ConcurrentDictionary keys. + + + + + + + + + + + + + + HttpUtility\HttpUtility.cs @@ -899,6 +917,11 @@ Addressed threading issue on reading ConcurrentDictionary keys. + + + + + @@ -923,7 +946,6 @@ Addressed threading issue on reading ConcurrentDictionary keys. - diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 609a09597..5646458ad 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -331,6 +331,10 @@ Addressed threading issue on reading ConcurrentDictionary keys. Enum\ResponseType.cs + + + + @@ -341,6 +345,20 @@ Addressed threading issue on reading ConcurrentDictionary keys. + + + + + + + + + + + + + + HttpUtility\HttpUtility.cs @@ -708,6 +726,11 @@ Addressed threading issue on reading ConcurrentDictionary keys. + + + + + @@ -716,7 +739,6 @@ Addressed threading issue on reading ConcurrentDictionary keys. - 14.0 From fd63a9b032bff17cdf04a5ab0485e250ea006558 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Fri, 23 Jun 2023 18:06:56 +0530 Subject: [PATCH 46/71] HandshakeReconnectingState and other Pascal casing (#172) * HandshakeReconnectingState * EmitStatusInvocation * HandshakeFailedState --- .../Effects/HandshakeEffectHandler.cs | 8 ++-- .../Subscribe/Events/SubscriptionEvents.cs | 16 ++++--- .../Invocations/SubscriptionInvocations.cs | 24 +++++++--- .../Subscribe/States/HandshakeFailedState.cs | 44 ++++++++++++++++++- .../States/HandshakeReconnectingState.cs | 42 ++++++++++++++---- .../Subscribe/States/HandshakingState.cs | 4 +- .../Subscribe/States/UnsubscribedState.cs | 8 ++-- 7 files changed, 114 insertions(+), 32 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index f09621caa..a28db2be5 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -20,12 +20,12 @@ public async Task Run(HandshakeInvocation invocation) { // TODO fix this, probably wrong :) var resp = await manager.HandshakeRequest( PNOperationType.PNSubscribeOperation, - invocation.channels.ToArray(), - invocation.channelGroups.ToArray(), + invocation.Channels.ToArray(), + invocation.ChannelGroups.ToArray(), null, null, - invocation.initialSubscribeQueryParams, - invocation.externalQueryParams + invocation.InitialSubscribeQueryParams, + invocation.ExternalQueryParams ); if (!resp.Item2.Error) { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index 86295c0b6..e2a174173 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -2,14 +2,14 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.Events { public class SubscriptionChangedEvent : Core.IEvent { - public IEnumerable channels; - public IEnumerable channelGroups; + public IEnumerable Channels; + public IEnumerable ChannelGroups; } public class SubscriptionRestoredEvent : Core.IEvent { - public IEnumerable channels; - public IEnumerable channelGroups; - public SubscriptionCursor cursor; + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; } public class HandshakeSuccessEvent : Core.IEvent { @@ -22,6 +22,8 @@ public class HandshakeFailureEvent : Core.IEvent { } public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; } public class HandshakeReconnectFailureEvent : HandshakeFailureEvent { @@ -31,6 +33,8 @@ public class HandshakeReconnectRetryEvent : Core.IEvent { } public class HandshakeReconnectGiveUpEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; // TODO status or reason? public PNStatus status; } @@ -65,5 +69,7 @@ public class DisconnectEvent : Core.IEvent { } public class ReconnectEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; } } \ 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 index cec835b12..39e615b2e 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -7,24 +7,34 @@ internal class EmitMessagesInvocation : Core.IEffectInvocation { } internal class EmitStatusInvocation : Core.IEffectInvocation { - + public IEnumerable Channels; + public IEnumerable ChannelGroups; } internal class HandshakeInvocation : Core.IEffectInvocation { - public IEnumerable channels; - public IEnumerable channelGroups; - public Dictionary initialSubscribeQueryParams = new Dictionary(); - public Dictionary externalQueryParams = new Dictionary(); + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public Dictionary InitialSubscribeQueryParams = new Dictionary(); + public Dictionary ExternalQueryParams = new Dictionary(); } - internal class ReceiveMessagesInvocation : Core.IEffectInvocation { } + internal class ReceiveMessagesInvocation : Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + } internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } internal class HandshakeCancelInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } //internal class ReconnectInvocation : Core.IEffectInvocation { } - internal class HandshakeReconnectInvocation: Core.IEffectInvocation { } + internal class HandshakeReconnectInvocation: Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + } + internal class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } internal class ReceiveReconnectInvocation: Core.IEffectInvocation { } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index 1ed4edbec..816d80fc2 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -12,7 +12,49 @@ internal class HandshakeFailedState : Core.IState { public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new HandshakingState() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + }, + new[] { + new HandshakeInvocation() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + }, + } + ); + case Events.ReconnectEvent reconnect: + return new Tuple>( + new HandshakingState() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + }, + new[] { + new HandshakeInvocation() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + }, + } + ); + + default: return null; + } } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index dfe2561c1..26aba2e9c 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -16,27 +16,51 @@ internal class HandshakeReconnectingState : Core.IState { case Events.SubscriptionChangedEvent subscriptionChanged: return new Tuple>( new HandshakingState() { - channels = subscriptionChanged.channels, - channelGroups = subscriptionChanged.channelGroups, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, }, new[] { new HandshakeInvocation() { - channels = subscriptionChanged.channels, - channelGroups = subscriptionChanged.channelGroups, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, }, } ); - case Events.DisconnectEvent disconnectEvent: + case Events.DisconnectEvent disconnect: return new Tuple>( new HandshakeStoppedState() { - Channels = disconnectEvent.Channels, - ChannelGroups = disconnectEvent.ChannelGroups + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups }, null ); - case Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUpEvent: + case Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUp: return new Tuple>( - new HandshakeFailedState() { }, + new HandshakeFailedState() { + Channels = handshakeReconnectGiveUp.Channels, + ChannelGroups = handshakeReconnectGiveUp.ChannelGroups + }, + null + ); + case Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess: + return new Tuple>( + new ReceivingState() { + Channels = handshakeReconnectSuccess.Channels, + ChannelGroups = handshakeReconnectSuccess.ChannelGroups + }, + new[] { + new EmitStatusInvocation() { + Channels = handshakeReconnectSuccess.Channels, + ChannelGroups = handshakeReconnectSuccess.ChannelGroups, + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new HandshakeFailedState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups + }, null ); diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 29aa611b2..a17469e96 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -5,8 +5,8 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { internal class HandshakingState : Core.IState { - public IEnumerable channels; - public IEnumerable channelGroups; + public IEnumerable Channels; + public IEnumerable ChannelGroups; public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index f8a45cd74..7c9a7c386 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -12,13 +12,13 @@ internal class UnsubscribedState : Core.IState { case Events.SubscriptionChangedEvent subscriptionChanged: return new Tuple>( new HandshakingState() { - channels = subscriptionChanged.channels, - channelGroups = subscriptionChanged.channelGroups, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, }, new[] { new HandshakeInvocation() { - channels = subscriptionChanged.channels, - channelGroups = subscriptionChanged.channelGroups, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, }, } ); From d62f48be189cd0e78da2057bd05b61f8372f86f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Fri, 23 Jun 2023 14:41:26 +0200 Subject: [PATCH 47/71] Null guard on invocation list --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index d257ff3c8..2e1ff47a2 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -10,6 +10,8 @@ internal abstract class Engine { private Task currentTransition; + private readonly IEffectInvocation[] emptyInvocationList = new IEffectInvocation[0]; + public Engine() { eventQueue.onEventQueued += OnEvent; } @@ -41,13 +43,13 @@ private async Task Transition(IEvent e) { /// Launch the invocations associated with transitioning between states /// private async Task ExecuteStateChange(IState sourceState, IState targetState, IEnumerable invocations) { - foreach (var effectInvocation in sourceState.OnExit) { + foreach (var effectInvocation in sourceState.OnExit ?? emptyInvocationList) { await dispatcher.Dispatch(effectInvocation); } - foreach (var effectInvocation in invocations) { + foreach (var effectInvocation in invocations ?? emptyInvocationList) { await dispatcher.Dispatch(effectInvocation); } - foreach (var effectInvocation in targetState.OnEntry) { + foreach (var effectInvocation in targetState.OnEntry ?? emptyInvocationList) { await dispatcher.Dispatch(effectInvocation); } } From de7006070cd61163f8ce0f240877d0f4998ba7c1 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 26 Jun 2023 20:28:57 +0530 Subject: [PATCH 48/71] HandshakeStoppedState, ReceiveStoppedState, ReceiveFailedState, ReceivingState and ReceiveReconnectState transitions (#173) * HandshakeStoppedState transitions * ReceiveStoppedState and ReceiveFailedState transitions * ReceivingState transitions * ReceiveReconnectState transitions --- .../Subscribe/Events/SubscriptionEvents.cs | 16 +++- .../Invocations/SubscriptionInvocations.cs | 9 +- .../Subscribe/States/HandshakeFailedState.cs | 5 +- .../States/HandshakeReconnectingState.cs | 6 +- .../Subscribe/States/HandshakeStoppedState.cs | 45 ++++++++- .../Subscribe/States/ReceiveFailedState.cs | 51 +++++++++- .../States/ReceiveReconnectingState.cs | 95 ++++++++++++++++++- .../Subscribe/States/ReceiveStoppedState.cs | 52 +++++++++- .../Subscribe/States/ReceivingState.cs | 74 ++++++++++++++- 9 files changed, 342 insertions(+), 11 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index e2a174173..ff3688cce 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -4,6 +4,7 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.Events { public class SubscriptionChangedEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; } public class SubscriptionRestoredEvent : Core.IEvent { @@ -24,6 +25,7 @@ public class HandshakeFailureEvent : Core.IEvent { public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; } public class HandshakeReconnectFailureEvent : HandshakeFailureEvent { @@ -40,11 +42,16 @@ public class HandshakeReconnectGiveUpEvent : Core.IEvent { } public class ReceiveSuccessEvent : Core.IEvent { - public List> messages; - public SubscriptionCursor cursor; + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public List> Messages; + public SubscriptionCursor Cursor; } public class ReceiveFailureEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; // TODO status or reason? public PNStatus status; } @@ -59,6 +66,9 @@ public class ReceiveReconnectFailureEvent : ReceiveFailureEvent { } public class ReceiveReconnectGiveUpEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; // TODO status or reason? public PNStatus status; } @@ -66,10 +76,12 @@ public class ReceiveReconnectGiveUpEvent : Core.IEvent { 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 index 39e615b2e..3ec6c133c 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -22,6 +22,7 @@ internal class ReceiveMessagesInvocation : Core.IEffectInvocation { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; } internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } @@ -37,7 +38,13 @@ internal class HandshakeReconnectInvocation: Core.IEffectInvocation internal class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } - internal class ReceiveReconnectInvocation: Core.IEffectInvocation { } + internal class ReceiveReconnectInvocation: Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + } + internal class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } //internal class CancelReconnectInvocation : ReconnectInvocation, 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 index 816d80fc2..8816c77db 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -8,6 +8,7 @@ internal class HandshakeFailedState : Core.IState { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } @@ -43,12 +44,14 @@ internal class HandshakeFailedState : Core.IState { return new Tuple>( new ReceivingState() { Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor }, new[] { new ReceiveMessagesInvocation() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor }, } ); diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index 26aba2e9c..db5f59d88 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -46,7 +46,8 @@ internal class HandshakeReconnectingState : Core.IState { return new Tuple>( new ReceivingState() { Channels = handshakeReconnectSuccess.Channels, - ChannelGroups = handshakeReconnectSuccess.ChannelGroups + ChannelGroups = handshakeReconnectSuccess.ChannelGroups, + Cursor = handshakeReconnectSuccess.Cursor }, new[] { new EmitStatusInvocation() { @@ -59,7 +60,8 @@ internal class HandshakeReconnectingState : Core.IState { return new Tuple>( new HandshakeFailedState() { Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor }, null ); diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 9774bc6cb..885acc5a8 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -12,7 +12,50 @@ internal class HandshakeStoppedState : Core.IState { public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new HandshakingState() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + }, + new[] { + new HandshakeInvocation() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + }, + } + ); + case Events.ReconnectEvent reconnect: + return new Tuple>( + new HandshakingState() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + }, + new[] { + new HandshakeInvocation() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + }, + } + ); + + default: return 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 index 41c4be2ee..8b3b68489 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -8,11 +8,60 @@ internal class ReceiveFailedState : Core.IState { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor + }, + } + ); + case Events.ReconnectEvent reconnect: + return new Tuple>( + new ReceivingState() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + } + ); + + default: return null; + } } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 2a16d3c02..d18036c5a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -8,11 +8,104 @@ internal class ReceiveReconnectingState : Core.IState { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor + }, + } + ); + case Events.DisconnectEvent disconnect: + return new Tuple>( + new ReceiveStoppedState() { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + Cursor = disconnect.Cursor + }, + new[] { + new EmitStatusInvocation() { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + } + ); + case Events.ReceiveReconnectSuccessEvent receiveReconnectSuccess: + return new Tuple>( + new ReceivingState() { + Channels = receiveReconnectSuccess.Channels, + ChannelGroups = receiveReconnectSuccess.ChannelGroups, + Cursor = receiveReconnectSuccess.Cursor + }, + new IEffectInvocation[] { + new EmitStatusInvocation() { + Channels = receiveReconnectSuccess.Channels, + ChannelGroups = receiveReconnectSuccess.ChannelGroups, + }, + new ReceiveMessagesInvocation() { + Channels = receiveReconnectSuccess.Channels, + ChannelGroups = receiveReconnectSuccess.ChannelGroups, + Cursor = receiveReconnectSuccess.Cursor + } + } + ); + case Events.ReceiveReconnectFailureEvent receiveReconnectFailure: + return new Tuple>( + new ReceiveReconnectingState() { + Channels = receiveReconnectFailure.Channels, + ChannelGroups = receiveReconnectFailure.ChannelGroups, + Cursor = receiveReconnectFailure.Cursor + }, + new[] + { + new ReceiveReconnectInvocation + { + Channels = receiveReconnectFailure.Channels, + ChannelGroups = receiveReconnectFailure.ChannelGroups, + Cursor = receiveReconnectFailure.Cursor + } + } + ); + case Events.ReceiveReconnectGiveUpEvent receiveReconnectGiveUp: + return new Tuple>( + new ReceiveFailedState() { + Channels = receiveReconnectGiveUp.Channels, + ChannelGroups = receiveReconnectGiveUp.ChannelGroups, + Cursor = receiveReconnectGiveUp.Cursor + }, + null + ); + + default: return null; + } } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 0d925e43e..a66411b36 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -8,11 +8,61 @@ internal class ReceiveStoppedState : Core.IState { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); + switch (e) { + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor, + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor, + }, + } + ); + case Events.ReconnectEvent reconnect: + return new Tuple>( + new ReceivingState() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + } + ); + + default: return null; + } } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index 2208f539a..06bee3d54 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -8,11 +8,83 @@ internal class ReceivingState : Core.IState { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); + switch (e) { + case Events.ReceiveSuccessEvent receiveSuccess: + return new Tuple>( + new ReceivingState() { + Channels = receiveSuccess.Channels, + ChannelGroups = receiveSuccess.ChannelGroups, + Cursor = receiveSuccess.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = receiveSuccess.Channels, + ChannelGroups = receiveSuccess.ChannelGroups, + Cursor = receiveSuccess.Cursor + }, + } + ); + case Events.SubscriptionChangedEvent subscriptionChanged: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = subscriptionChanged.Cursor + }, + } + ); + case Events.SubscriptionRestoredEvent subscriptionRestored: + return new Tuple>( + new ReceivingState() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + new[] { + new ReceiveMessagesInvocation() { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }, + } + ); + case Events.DisconnectEvent disconnect: + return new Tuple>( + new ReceiveStoppedState() { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + Cursor = disconnect.Cursor + }, + new[] { + new EmitStatusInvocation() { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + }, + } + ); + case Events.ReceiveFailureEvent receiveFailure: + return new Tuple>( + new ReceiveReconnectingState() { + Channels = receiveFailure.Channels, + ChannelGroups = receiveFailure.ChannelGroups, + Cursor = receiveFailure.Cursor + }, + null + ); + + default: return null; + } } } } From 3a944e606cd1465e2a0333795b600060b35394f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= <106665140+MikeDobrzan@users.noreply.github.com> Date: Tue, 27 Jun 2023 15:05:25 +0200 Subject: [PATCH 49/71] Eventengine/state simplification (#174) * WIP remove abstract from interface, add transition utils * Simplified the invocations during transitions, added onEntry and onExit transitions, added HandshakingState and UnsubscribedState * fix * Switch expression * WIP * WIP * WIP * Review fixes * member case * removed With(null) --- .../EventEngine/Core/EventEngineInterfaces.cs | 9 +- src/Api/PubnubApi/EventEngine/Core/Utils.cs | 19 ++ .../Effects/HandshakeEffectHandler.cs | 2 +- .../Subscribe/Events/SubscriptionEvents.cs | 28 ++- .../Invocations/SubscriptionInvocations.cs | 31 +++- .../Subscribe/States/HandshakeFailedState.cs | 88 ++++------ .../States/HandshakeReconnectingState.cs | 112 ++++++------ .../Subscribe/States/HandshakeStoppedState.cs | 84 ++++----- .../Subscribe/States/HandshakingState.cs | 56 ++++-- .../Subscribe/States/ReceiveFailedState.cs | 96 ++++------- .../States/ReceiveReconnectingState.cs | 163 +++++++----------- .../Subscribe/States/ReceiveStoppedState.cs | 99 +++++------ .../Subscribe/States/ReceivingState.cs | 138 ++++++--------- .../Subscribe/States/SubscribedState.cs | 13 -- .../Subscribe/States/UnsubscribedState.cs | 49 +++--- .../Subscribe/SubscribeEventEngine.cs | 2 +- 16 files changed, 438 insertions(+), 551 deletions(-) create mode 100644 src/Api/PubnubApi/EventEngine/Core/Utils.cs delete mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 201f027de..c71f0df24 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using System.Collections.Generic; namespace PubnubApi.PubnubEventEngine.Core { @@ -17,14 +18,14 @@ internal interface IEffectCancelInvocation : IEffectInvocation { } internal interface IEvent { }; internal interface IState { - public abstract IEnumerable OnEntry { get; } - public abstract IEnumerable OnExit { get; } + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } /// /// The EE transition pure function. /// /// Input event /// Target state and invocation list, or null for no-transition - public abstract System.Tuple> Transition(IEvent e); + public System.Tuple> Transition(IEvent e); } } \ 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..0a780331b --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace PubnubApi.PubnubEventEngine.Core +{ + internal static class Utils + { + internal static Tuple> With(this IState state, params IEffectInvocation[] invocations) + { + return new Tuple>(state, invocations); + } + + internal static IEffectInvocation[] AsArray(this IEffectInvocation invocation) + { + return new IEffectInvocation[] { invocation }; + } + } +} \ 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 index a28db2be5..72a15f8ce 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -37,7 +37,7 @@ public async Task Run(HandshakeInvocation invocation) { Timetoken = handshakeResponse.Timetoken.Timestamp }; - eventQueue.Enqueue(new Events.HandshakeSuccessEvent() {cursor = c}); + eventQueue.Enqueue(new Events.HandshakeSuccessEvent() {Cursor = c}); } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index ff3688cce..42739802d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -4,7 +4,6 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.Events { public class SubscriptionChangedEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; } public class SubscriptionRestoredEvent : Core.IEvent { @@ -14,31 +13,28 @@ public class SubscriptionRestoredEvent : Core.IEvent { } public class HandshakeSuccessEvent : Core.IEvent { - public SubscriptionCursor cursor; + public SubscriptionCursor Cursor; + public PNStatus Status; } public class HandshakeFailureEvent : Core.IEvent { - // TODO status or reason? - public PNStatus status; + public PNStatus Status; } public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { - public IEnumerable Channels; - public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; } - public class HandshakeReconnectFailureEvent : HandshakeFailureEvent { + public class HandshakeReconnectFailureEvent : HandshakeFailureEvent + { + public PNStatus Status; } public class HandshakeReconnectRetryEvent : Core.IEvent { } public class HandshakeReconnectGiveUpEvent : Core.IEvent { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - // TODO status or reason? - public PNStatus status; + public PNStatus Status; } public class ReceiveSuccessEvent : Core.IEvent { @@ -46,14 +42,11 @@ public class ReceiveSuccessEvent : Core.IEvent { public IEnumerable ChannelGroups; public List> Messages; public SubscriptionCursor Cursor; + public PNStatus Status; } public class ReceiveFailureEvent : Core.IEvent { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; - // TODO status or reason? - public PNStatus status; + public PNStatus Status; } public class ReceiveReconnectRetry : Core.IEvent { @@ -69,8 +62,7 @@ public class ReceiveReconnectGiveUpEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - // TODO status or reason? - public PNStatus status; + public PNStatus Status; } public class DisconnectEvent : Core.IEvent { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs index 3ec6c133c..7fc22eceb 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -3,17 +3,38 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.Invocations { internal class EmitMessagesInvocation : Core.IEffectInvocation { - public List messages; + public IEnumerable> Messages; + + public EmitMessagesInvocation(IEnumerable> messages) + { + this.Messages = messages; + } } internal class EmitStatusInvocation : Core.IEffectInvocation { - public IEnumerable Channels; - public IEnumerable ChannelGroups; + // TODO merge status variables into one? + public PNStatusCategory StatusCategory; + public PNStatus Status; + + public EmitStatusInvocation(PNStatus status) + { + this.Status = status; + } + + public EmitStatusInvocation(PNStatusCategory category) + { + this.StatusCategory = category; + this.Status = new PNStatus() + { + Category = category, + }; + } } internal 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(); } @@ -27,9 +48,8 @@ internal class ReceiveMessagesInvocation : Core.IEffectInvocation internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } - internal class HandshakeCancelInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } + internal class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } - //internal class ReconnectInvocation : Core.IEffectInvocation { } internal class HandshakeReconnectInvocation: Core.IEffectInvocation { public IEnumerable Channels; @@ -46,5 +66,4 @@ internal class ReceiveReconnectInvocation: Core.IEffectInvocation } internal class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } - //internal class CancelReconnectInvocation : ReconnectInvocation, 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 index 8816c77db..b21ca1c8b 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -3,61 +3,39 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakeFailedState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class HandshakeFailedState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - new[] { - new HandshakeInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - } - ); - case Events.ReconnectEvent reconnect: - return new Tuple>( - new HandshakingState() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - }, - new[] { - new HandshakeInvocation() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - } - ); + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, + }.With(), - default: return null; - } - } - } -} + Events.ReconnectEvent reconnect => new HandshakingState() + { + Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, + }.With(), + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + _ => 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 index db5f59d88..89d8092f6 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -3,71 +3,55 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakeReconnectingState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class HandshakeReconnectingState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; - public IEnumerable Channels; - public IEnumerable ChannelGroups; + public IEnumerable OnEntry => new HandshakeReconnectInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); + public IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - new[] { - new HandshakeInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - } - ); - case Events.DisconnectEvent disconnect: - return new Tuple>( - new HandshakeStoppedState() { - Channels = disconnect.Channels, - ChannelGroups = disconnect.ChannelGroups - }, - null - ); - case Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUp: - return new Tuple>( - new HandshakeFailedState() { - Channels = handshakeReconnectGiveUp.Channels, - ChannelGroups = handshakeReconnectGiveUp.ChannelGroups - }, - null - ); - case Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess: - return new Tuple>( - new ReceivingState() { - Channels = handshakeReconnectSuccess.Channels, - ChannelGroups = handshakeReconnectSuccess.ChannelGroups, - Cursor = handshakeReconnectSuccess.Cursor - }, - new[] { - new EmitStatusInvocation() { - Channels = handshakeReconnectSuccess.Channels, - ChannelGroups = handshakeReconnectSuccess.ChannelGroups, - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new HandshakeFailedState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - null - ); + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, + }.With(), - default: return null; - } - } - } + Events.DisconnectEvent disconnect => new HandshakeStoppedState() + { + Channels = disconnect.Channels, ChannelGroups = disconnect.ChannelGroups + }.With(), + + Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUp => new HandshakeFailedState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups + }.With( + new EmitStatusInvocation(handshakeReconnectGiveUp.Status) + ), + + Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess => new ReceivingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = handshakeReconnectSuccess.Cursor + }.With(new EmitStatusInvocation(handshakeReconnectSuccess.Status)), + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + _ => 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 index 885acc5a8..91df0fca8 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -3,59 +3,39 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakeStoppedState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class HandshakeStoppedState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; - public IEnumerable Channels; - public IEnumerable ChannelGroups; + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - new[] { - new HandshakeInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - } - ); - case Events.ReconnectEvent reconnect: - return new Tuple>( - new HandshakingState() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - }, - new[] { - new HandshakeInvocation() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - }, - } - ); + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, + }.With(), - default: return null; - } - } - } + Events.ReconnectEvent reconnect => new HandshakingState() + { + Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, + }.With(), + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + _ => 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 index a17469e96..154282a13 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -1,17 +1,53 @@ using System; using System.Collections.Generic; using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakingState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class HandshakingState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; - public IEnumerable Channels; - public IEnumerable ChannelGroups; + public IEnumerable OnEntry => new HandshakeInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); - } - } + public IEnumerable OnExit { get; } = new CancelHandshakeInvocation().AsArray(); + + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new States.HandshakingState() + { + Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups + }.With(), + + Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeFailedState() + { + Channels = this.Channels, ChannelGroups = this.ChannelGroups + }.With(new EmitStatusInvocation(handshakeFailure.Status)), + + Events.DisconnectEvent disconnect => new States.HandshakeStoppedState() + { + Channels = disconnect.Channels, ChannelGroups = disconnect.ChannelGroups, + }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), + + Events.HandshakeSuccessEvent success => new ReceivingState() + { + Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = success.Cursor + }.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 index 8b3b68489..3bb0c7c3a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -3,66 +3,44 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceiveFailedState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class ReceiveFailedState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor - }, - } - ); - case Events.ReconnectEvent reconnect: - return new Tuple>( - new ReceivingState() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - Cursor = reconnect.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - Cursor = reconnect.Cursor - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - } - ); + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor + }.With(), - default: return null; - } - } - } -} + Events.ReconnectEvent reconnect => new ReceivingState() + { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor + }.With(), + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + _ => 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 index d18036c5a..159d229cc 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -3,111 +3,68 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceiveReconnectingState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class ReceiveReconnectingState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; + public IEnumerable OnEntry => new ReceiveReconnectInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor - }, - } - ); - case Events.DisconnectEvent disconnect: - return new Tuple>( - new ReceiveStoppedState() { - Channels = disconnect.Channels, - ChannelGroups = disconnect.ChannelGroups, - Cursor = disconnect.Cursor - }, - new[] { - new EmitStatusInvocation() { - Channels = disconnect.Channels, - ChannelGroups = disconnect.ChannelGroups, - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - } - ); - case Events.ReceiveReconnectSuccessEvent receiveReconnectSuccess: - return new Tuple>( - new ReceivingState() { - Channels = receiveReconnectSuccess.Channels, - ChannelGroups = receiveReconnectSuccess.ChannelGroups, - Cursor = receiveReconnectSuccess.Cursor - }, - new IEffectInvocation[] { - new EmitStatusInvocation() { - Channels = receiveReconnectSuccess.Channels, - ChannelGroups = receiveReconnectSuccess.ChannelGroups, - }, - new ReceiveMessagesInvocation() { - Channels = receiveReconnectSuccess.Channels, - ChannelGroups = receiveReconnectSuccess.ChannelGroups, - Cursor = receiveReconnectSuccess.Cursor - } - } - ); - case Events.ReceiveReconnectFailureEvent receiveReconnectFailure: - return new Tuple>( - new ReceiveReconnectingState() { - Channels = receiveReconnectFailure.Channels, - ChannelGroups = receiveReconnectFailure.ChannelGroups, - Cursor = receiveReconnectFailure.Cursor - }, - new[] - { - new ReceiveReconnectInvocation - { - Channels = receiveReconnectFailure.Channels, - ChannelGroups = receiveReconnectFailure.ChannelGroups, - Cursor = receiveReconnectFailure.Cursor - } - } - ); - case Events.ReceiveReconnectGiveUpEvent receiveReconnectGiveUp: - return new Tuple>( - new ReceiveFailedState() { - Channels = receiveReconnectGiveUp.Channels, - ChannelGroups = receiveReconnectGiveUp.ChannelGroups, - Cursor = receiveReconnectGiveUp.Cursor - }, - null - ); + public IEnumerable OnExit { get; } = + new CancelReceiveReconnectInvocation().AsArray(); - default: return null; - } - } - } -} + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor + }.With(), + Events.DisconnectEvent disconnect => new ReceiveStoppedState() + { + Channels = disconnect.Channels, + ChannelGroups = disconnect.ChannelGroups, + Cursor = disconnect.Cursor + }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + Events.ReceiveReconnectSuccessEvent receiveReconnectSuccess => new ReceivingState() + { + Channels = receiveReconnectSuccess.Channels, + ChannelGroups = receiveReconnectSuccess.ChannelGroups, + Cursor = receiveReconnectSuccess.Cursor + }.With(new EmitStatusInvocation(receiveReconnectSuccess.Status)), + + Events.ReceiveReconnectFailureEvent receiveReconnectFailure => new ReceiveReconnectingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor + }.With(new EmitStatusInvocation(receiveReconnectFailure.Status)), + + Events.ReceiveReconnectGiveUpEvent receiveReconnectGiveUp => new ReceiveFailedState() + { + Channels = receiveReconnectGiveUp.Channels, + ChannelGroups = receiveReconnectGiveUp.ChannelGroups, + Cursor = receiveReconnectGiveUp.Cursor + }.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 index a66411b36..9684a0ad0 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -3,66 +3,45 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceiveStoppedState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class ReceiveStoppedState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor, - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor, - }, - } - ); - case Events.ReconnectEvent reconnect: - return new Tuple>( - new ReceivingState() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - Cursor = reconnect.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = reconnect.Channels, - ChannelGroups = reconnect.ChannelGroups, - Cursor = reconnect.Cursor - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - } - ); - - default: return null; - } - } - } -} + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor, + }.With(), + + Events.ReconnectEvent reconnect => new ReceivingState() + { + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor + }.With(), + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + _ => 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 index 06bee3d54..882eb3987 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -3,89 +3,63 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceivingState : Core.IState { +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class ReceivingState : Core.IState + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; + public IEnumerable OnEntry => new ReceiveMessagesInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - switch (e) { - case Events.ReceiveSuccessEvent receiveSuccess: - return new Tuple>( - new ReceivingState() { - Channels = receiveSuccess.Channels, - ChannelGroups = receiveSuccess.ChannelGroups, - Cursor = receiveSuccess.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = receiveSuccess.Channels, - ChannelGroups = receiveSuccess.ChannelGroups, - Cursor = receiveSuccess.Cursor - }, - } - ); - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = subscriptionChanged.Cursor - }, - } - ); - case Events.SubscriptionRestoredEvent subscriptionRestored: - return new Tuple>( - new ReceivingState() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - new[] { - new ReceiveMessagesInvocation() { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, - } - ); - case Events.DisconnectEvent disconnect: - return new Tuple>( - new ReceiveStoppedState() { - Channels = disconnect.Channels, - ChannelGroups = disconnect.ChannelGroups, - Cursor = disconnect.Cursor - }, - new[] { - new EmitStatusInvocation() { - Channels = disconnect.Channels, - ChannelGroups = disconnect.ChannelGroups, - }, - } - ); - case Events.ReceiveFailureEvent receiveFailure: - return new Tuple>( - new ReceiveReconnectingState() { - Channels = receiveFailure.Channels, - ChannelGroups = receiveFailure.ChannelGroups, - Cursor = receiveFailure.Cursor - }, - null - ); + public IEnumerable OnExit { get; } = new CancelReceiveMessagesInvocation().AsArray(); - default: return null; - } - } - } -} + public Tuple> Transition(IEvent e) + { + return e switch + { + Events.ReceiveSuccessEvent receiveSuccess => new ReceivingState() + { + Channels = receiveSuccess.Channels, + ChannelGroups = receiveSuccess.ChannelGroups, + Cursor = receiveSuccess.Cursor + }.With( + new EmitMessagesInvocation(receiveSuccess.Messages), + new EmitStatusInvocation(receiveSuccess.Status) + ), + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + { + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + Cursor = this.Cursor + }.With(), + + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + Events.DisconnectEvent disconnect => new ReceiveStoppedState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor + }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), + + Events.ReceiveFailureEvent receiveFailure => new ReceiveReconnectingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor + }.With(), + + _ => null + }; + } + } +} \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs deleted file mode 100644 index 39de39a23..000000000 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/SubscribedState.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; - -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class SubscribedState : Core.IState { - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) { - throw new NotImplementedException(); - } - } -} \ 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 index 7c9a7c386..757ce04ce 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -3,28 +3,31 @@ using PubnubApi.PubnubEventEngine.Core; using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class UnsubscribedState : Core.IState { - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - public Tuple> Transition(Core.IEvent e) { - switch (e) { - case Events.SubscriptionChangedEvent subscriptionChanged: - return new Tuple>( - new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - new[] { - new HandshakeInvocation() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - }, - } - ); +namespace PubnubApi.PubnubEventEngine.Subscribe.States +{ + internal class UnsubscribedState : Core.IState + { + public IEnumerable OnEntry { get; } + public IEnumerable OnExit { get; } - default: return null; - } - } - } + public Tuple> Transition(Core.IEvent e) + { + return e switch + { + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() + { + Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, + }.With(), + + Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() + { + Channels = subscriptionRestored.Channels, + ChannelGroups = subscriptionRestored.ChannelGroups, + Cursor = subscriptionRestored.Cursor + }.With(), + + _ => 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 index d0756455a..f71bd366f 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -13,7 +13,7 @@ public SubscribeEventEngine(SubscribeManager2 subscribeManager) { // initialize the handler, pass dependencies var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, eventQueue); dispatcher.Register(handshakeHandler); - dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); currentState = new UnsubscribedState(); } From 8500c5599325afeeff14f5b998ec546e32e2a8a8 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Tue, 27 Jun 2023 18:37:42 +0530 Subject: [PATCH 50/71] file ref fix --- src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 2 +- src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index fd3bba484..75f021e2e 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -218,6 +218,7 @@ Addressed threading issue on reading ConcurrentDictionary keys. + @@ -239,7 +240,6 @@ Addressed threading issue on reading ConcurrentDictionary keys. - diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 5646458ad..2b28b1763 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -335,6 +335,7 @@ Addressed threading issue on reading ConcurrentDictionary keys. + @@ -356,7 +357,6 @@ Addressed threading issue on reading ConcurrentDictionary keys. - From 2fc23b95348eab19551a34f1ed91fddc001c2f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 11:47:41 +0200 Subject: [PATCH 51/71] fix: simplify and add comments --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 6 ++---- .../EventEngine/Core/EventEngineInterfaces.cs | 14 ++++++++++++++ src/Api/PubnubApi/EventEngine/Core/Utils.cs | 7 +++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 2e1ff47a2..ce8cc2ea5 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -8,7 +8,7 @@ internal abstract class Engine { protected EffectDispatcher dispatcher = new EffectDispatcher(); protected IState currentState; - private Task currentTransition; + private Task currentTransition = Utils.EmptyTask; private readonly IEffectInvocation[] emptyInvocationList = new IEffectInvocation[0]; @@ -21,9 +21,7 @@ public Engine() { } private async void OnEvent(EventQueue q) { - if (!(currentTransition is null)) { - await currentTransition; - } + await currentTransition; currentTransition = Transition(q.Dequeue()).ContinueWith((res) => currentState = res.Result); } diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index c71f0df24..85bc64f9f 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -3,16 +3,30 @@ using System.Collections.Generic; namespace PubnubApi.PubnubEventEngine.Core { + + /// + /// Generic effect handler. + /// internal interface IEffectHandler { Task Cancel(); } + /// + /// Handler (implementation) for a given invocation. The invocation represents the input arguments of a handler. + /// + /// Associated invocation internal interface IEffectHandler : IEffectHandler where T : IEffectInvocation { Task Run(T invocation); } + /// + /// An effect invocation. It represents calling Run() on a registered effect handler - calling it is orchestrated by the dispatcher. + /// internal interface IEffectInvocation { } + /// + /// A cancel effect invocation. It represents calling Cancel() on a registered effect handler - calling it is orchestrated by the dispatcher. + /// internal interface IEffectCancelInvocation : IEffectInvocation { } internal interface IEvent { }; diff --git a/src/Api/PubnubApi/EventEngine/Core/Utils.cs b/src/Api/PubnubApi/EventEngine/Core/Utils.cs index 0a780331b..83b0159d2 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Utils.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -6,6 +6,13 @@ namespace PubnubApi.PubnubEventEngine.Core { internal static class Utils { + static Utils() + { + EmptyTask.Start(); + } + + internal static Task EmptyTask { get; } = new Task(() => null); + internal static Tuple> With(this IState state, params IEffectInvocation[] invocations) { return new Tuple>(state, invocations); From 9a8a05458026aa55a8566497d313e33c3c919b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 11:49:59 +0200 Subject: [PATCH 52/71] fix: comments --- src/Api/PubnubApi/EventEngine/Core/EventQueue.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index 1386d300c..27ed5f6ea 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -8,6 +8,10 @@ internal class EventQueue { 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? From 3d831bb7cbb28ffb6538e971fae63ae2aa82b2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 11:58:05 +0200 Subject: [PATCH 53/71] fix: more comments --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index ce8cc2ea5..527ce63fd 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -21,6 +21,7 @@ public Engine() { } private async void OnEvent(EventQueue q) { + // await the current running transition - non blocking await currentTransition; currentTransition = Transition(q.Dequeue()).ContinueWith((res) => currentState = res.Result); } From b64812e21606f38172fc692994708cafe489a34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 13:46:57 +0200 Subject: [PATCH 54/71] fix: race condition --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 11 +-- .../PubnubApi/EventEngine/Core/EventQueue.cs | 74 +++++++++++++------ 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 527ce63fd..6bfc56f3e 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -8,7 +8,7 @@ internal abstract class Engine { protected EffectDispatcher dispatcher = new EffectDispatcher(); protected IState currentState; - private Task currentTransition = Utils.EmptyTask; + private bool transitioning = false; private readonly IEffectInvocation[] emptyInvocationList = new IEffectInvocation[0]; @@ -20,10 +20,11 @@ public Engine() { eventQueue.onEventQueued -= OnEvent; } - private async void OnEvent(EventQueue q) { - // await the current running transition - non blocking - await currentTransition; - currentTransition = Transition(q.Dequeue()).ContinueWith((res) => currentState = res.Result); + private async void OnEvent(EventQueue q) + { + if (eventQueue.IsLooping) return; + + await eventQueue.Loop(async e => currentState = await Transition(e)); } private async Task Transition(IEvent e) { diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index 27ed5f6ea..059861c4b 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -1,29 +1,59 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; -namespace PubnubApi.PubnubEventEngine.Core { - internal class EventQueue { - private volatile Queue eventQueue = new Queue(); - private object lockObj = new object(); +namespace PubnubApi.PubnubEventEngine.Core +{ + internal class EventQueue + { + private volatile Queue eventQueue = new Queue(); + private object lockObj = new object(); + + public bool IsLooping { get; private set; } - public event System.Action onEventQueued; + 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); - } - } + /// + /// 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); + } + } - public IEvent Dequeue() { - lock (lockObj) { - return eventQueue.Any() ? eventQueue.Dequeue() : null; - } - } - } + private IEvent Dequeue() + { + lock (lockObj) + { + return eventQueue.Any() ? eventQueue.Dequeue() : null; + } + } + + public async Task Loop(System.Func function) + { + IsLooping = true; + while (Count > 0) + { + await function(Dequeue()); + } + IsLooping = false; + } + + public int Count + { + get + { + lock (lockObj) + { + return eventQueue.Count; + } + } + } + } } \ No newline at end of file From 8f1e3f3504c9fcd9c5ad1bd37d5fff9f3f0f177a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 13:54:45 +0200 Subject: [PATCH 55/71] fix: additional race condition guards --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 11 +++++------ src/Api/PubnubApi/EventEngine/Core/EventQueue.cs | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 6bfc56f3e..4cc1e6ef4 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -8,23 +8,22 @@ internal abstract class Engine { protected EffectDispatcher dispatcher = new EffectDispatcher(); protected IState currentState; - private bool transitioning = false; + private Task currentTransitionLoop = Utils.EmptyTask; private readonly IEffectInvocation[] emptyInvocationList = new IEffectInvocation[0]; public Engine() { - eventQueue.onEventQueued += OnEvent; + eventQueue.OnEventQueued += OnEvent; } ~Engine() { - eventQueue.onEventQueued -= OnEvent; + eventQueue.OnEventQueued -= OnEvent; } private async void OnEvent(EventQueue q) { - if (eventQueue.IsLooping) return; - - await eventQueue.Loop(async e => currentState = await Transition(e)); + await currentTransitionLoop; + currentTransitionLoop = eventQueue.Loop(async e => currentState = await Transition(e)); } private async Task Transition(IEvent e) { diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index 059861c4b..8ff943a13 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -11,7 +11,7 @@ internal class EventQueue public bool IsLooping { get; private set; } - public event System.Action onEventQueued; + public event System.Action OnEventQueued; /// /// Enqueue (fire) an event to the Event Engine. Handling that event is covered by the Engine itself. @@ -23,7 +23,7 @@ public void Enqueue(IEvent e) { // TODO de-dupe? Throttle? eventQueue.Enqueue(e); - onEventQueued?.Invoke(this); + OnEventQueued?.Invoke(this); } } From 5c0892b7af62b3b136d223a39a3149d1c9c33487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 13:55:55 +0200 Subject: [PATCH 56/71] fix: removed unused property --- src/Api/PubnubApi/EventEngine/Core/EventQueue.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index 8ff943a13..4dcd845f8 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -8,8 +8,6 @@ internal class EventQueue { private volatile Queue eventQueue = new Queue(); private object lockObj = new object(); - - public bool IsLooping { get; private set; } public event System.Action OnEventQueued; @@ -37,12 +35,10 @@ private IEvent Dequeue() public async Task Loop(System.Func function) { - IsLooping = true; while (Count > 0) { await function(Dequeue()); } - IsLooping = false; } public int Count From aa2c1c55835d0a2956569af971ad346a90f08393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 6 Jul 2023 14:00:41 +0200 Subject: [PATCH 57/71] fix: made loop generic --- src/Api/PubnubApi/EventEngine/Core/EventQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index 4dcd845f8..a0eb2fe64 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -33,7 +33,7 @@ private IEvent Dequeue() } } - public async Task Loop(System.Func function) + public async Task Loop(System.Func> function) { while (Count > 0) { From 8ba35de21502a7c46f53af89bd864972d45aa65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= <106665140+MikeDobrzan@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:42:09 +0200 Subject: [PATCH 58/71] feat: simplified abstraction (#176) --- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 10 ++-- .../EventEngine/Core/EventEngineInterfaces.cs | 19 +++++-- src/Api/PubnubApi/EventEngine/Core/Utils.cs | 49 +++++++++++++++++-- .../Subscribe/States/HandshakeFailedState.cs | 13 ++--- .../States/HandshakeReconnectingState.cs | 14 +++--- .../Subscribe/States/HandshakeStoppedState.cs | 15 +++--- .../Subscribe/States/HandshakingState.cs | 12 ++--- .../Subscribe/States/ReceiveFailedState.cs | 10 ++-- .../States/ReceiveReconnectingState.cs | 12 ++--- .../Subscribe/States/ReceiveStoppedState.cs | 14 ++---- .../Subscribe/States/ReceivingState.cs | 14 +++--- .../Subscribe/States/UnsubscribedState.cs | 11 ++--- 12 files changed, 115 insertions(+), 78 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 4cc1e6ef4..f0315ac13 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -6,7 +6,7 @@ internal abstract class Engine { public EventQueue eventQueue = new EventQueue(); protected EffectDispatcher dispatcher = new EffectDispatcher(); - protected IState currentState; + protected State currentState; private Task currentTransitionLoop = Utils.EmptyTask; @@ -26,22 +26,22 @@ private async void OnEvent(EventQueue q) currentTransitionLoop = eventQueue.Loop(async e => currentState = await Transition(e)); } - private async Task Transition(IEvent e) { + private async Task Transition(IEvent e) { var stateInvocationPair = currentState.Transition(e); if (stateInvocationPair is null) { return currentState; } - await ExecuteStateChange(currentState, stateInvocationPair.Item1, stateInvocationPair.Item2); + await ExecuteStateChange(currentState, stateInvocationPair.State, stateInvocationPair.Invocations); - return stateInvocationPair.Item1; + return stateInvocationPair.State; } /// /// Launch the invocations associated with transitioning between states /// - private async Task ExecuteStateChange(IState sourceState, IState targetState, IEnumerable invocations) { + private async Task ExecuteStateChange(State sourceState, State targetState, IEnumerable invocations) { foreach (var effectInvocation in sourceState.OnExit ?? emptyInvocationList) { await dispatcher.Dispatch(effectInvocation); } diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 85bc64f9f..4dbc203b9 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -31,15 +31,26 @@ internal interface IEffectCancelInvocation : IEffectInvocation { } internal interface IEvent { }; - internal interface IState { - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } + internal 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 System.Tuple> Transition(IEvent e); + 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/Utils.cs b/src/Api/PubnubApi/EventEngine/Core/Utils.cs index 83b0159d2..f3edacb10 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Utils.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -11,16 +11,55 @@ static Utils() EmptyTask.Start(); } - internal static Task EmptyTask { get; } = new Task(() => null); + internal static Task EmptyTask { get; } = new Task(() => null); + + internal static IEffectInvocation[] AsArray(this IEffectInvocation invocation) + { + return new IEffectInvocation[] { invocation }; + } + } + + internal class TransitionResult + { + public State State => tuple.Item1; + public IEnumerable Invocations => tuple.Item2; - internal static Tuple> With(this IState state, params IEffectInvocation[] invocations) + private readonly Tuple> tuple; + + /// + /// Create a state-invocation pair with empty invocations + /// + public TransitionResult(State state) { - return new Tuple>(state, invocations); + this.tuple = new Tuple>(state, new IEffectInvocation[0]); } - internal static IEffectInvocation[] AsArray(this IEffectInvocation invocation) + /// + /// Create a state-invocation pair + /// + public TransitionResult(State state, IEnumerable invocations) { - return new IEffectInvocation[] { invocation }; + 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/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index b21ca1c8b..a62e27b18 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -5,34 +5,31 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakeFailedState : Core.IState + internal class HandshakeFailedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - }.With(), + }, Events.ReconnectEvent reconnect => new HandshakingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index 89d8092f6..ae8fd3524 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -5,28 +5,28 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakeReconnectingState : Core.IState + internal class HandshakeReconnectingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; - public IEnumerable OnEntry => new HandshakeReconnectInvocation() + public override IEnumerable OnEntry => new HandshakeReconnectInvocation() { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); - public IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); + public override IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - }.With(), + }, Events.DisconnectEvent disconnect => new HandshakeStoppedState() { Channels = disconnect.Channels, ChannelGroups = disconnect.ChannelGroups - }.With(), + }, Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUp => new HandshakeFailedState() { @@ -48,7 +48,7 @@ internal class HandshakeReconnectingState : Core.IState Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 91df0fca8..f58100818 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -5,34 +5,31 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakeStoppedState : Core.IState + internal class HandshakeStoppedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; - - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - - public Tuple> Transition(IEvent e) + + public override TransitionResult Transition(IEvent e) { return e switch { Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - }.With(), + }, Events.ReconnectEvent reconnect => new HandshakingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 154282a13..7b2584c52 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -5,31 +5,31 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class HandshakingState : Core.IState + internal class HandshakingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; - public IEnumerable OnEntry => new HandshakeInvocation() + public override IEnumerable OnEntry => new HandshakeInvocation() { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); - public IEnumerable OnExit { get; } = new CancelHandshakeInvocation().AsArray(); + public override IEnumerable OnExit { get; } = new CancelHandshakeInvocation().AsArray(); - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { Events.SubscriptionChangedEvent subscriptionChanged => new States.HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeFailedState() { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index 3bb0c7c3a..6c7e4632c 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -5,7 +5,7 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceiveFailedState : Core.IState + internal class ReceiveFailedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; @@ -14,7 +14,7 @@ internal class ReceiveFailedState : Core.IState public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { @@ -23,21 +23,21 @@ internal class ReceiveFailedState : Core.IState Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, Cursor = this.Cursor - }.With(), + }, Events.ReconnectEvent reconnect => new ReceivingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, Cursor = reconnect.Cursor - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 159d229cc..7d9bbaf9d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -5,19 +5,19 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceiveReconnectingState : Core.IState + internal class ReceiveReconnectingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - public IEnumerable OnEntry => new ReceiveReconnectInvocation() + public override IEnumerable OnEntry => new ReceiveReconnectInvocation() { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); - public IEnumerable OnExit { get; } = + public override IEnumerable OnExit { get; } = new CancelReceiveReconnectInvocation().AsArray(); - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { @@ -26,7 +26,7 @@ internal class ReceiveReconnectingState : Core.IState Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, Cursor = this.Cursor - }.With(), + }, Events.DisconnectEvent disconnect => new ReceiveStoppedState() { @@ -40,7 +40,7 @@ internal class ReceiveReconnectingState : Core.IState Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, Events.ReceiveReconnectSuccessEvent receiveReconnectSuccess => new ReceivingState() { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 9684a0ad0..06c6daded 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -5,17 +5,13 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceiveStoppedState : Core.IState + internal class ReceiveStoppedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { @@ -24,21 +20,21 @@ internal class ReceiveStoppedState : Core.IState Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, Cursor = this.Cursor, - }.With(), + }, Events.ReconnectEvent reconnect => new ReceivingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, Cursor = reconnect.Cursor - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index 882eb3987..428507ddb 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -5,18 +5,18 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class ReceivingState : Core.IState + internal class ReceivingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - public IEnumerable OnEntry => new ReceiveMessagesInvocation() + public override IEnumerable OnEntry => new ReceiveMessagesInvocation() { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); - public IEnumerable OnExit { get; } = new CancelReceiveMessagesInvocation().AsArray(); + public override IEnumerable OnExit { get; } = new CancelReceiveMessagesInvocation().AsArray(); - public Tuple> Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { @@ -35,14 +35,14 @@ internal class ReceivingState : Core.IState Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, Cursor = this.Cursor - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, Events.DisconnectEvent disconnect => new ReceiveStoppedState() { @@ -56,7 +56,7 @@ internal class ReceivingState : Core.IState Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor - }.With(), + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 757ce04ce..2a5affc92 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -5,26 +5,23 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { - internal class UnsubscribedState : Core.IState + internal class UnsubscribedState : Core.State { - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } - - public Tuple> Transition(Core.IEvent e) + public override TransitionResult Transition(Core.IEvent e) { return e switch { Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - }.With(), + }, Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor - }.With(), + }, _ => null }; From 839dda58ece0511f824ca23c2347fad12fc68402 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 17 Jul 2023 11:19:54 +0530 Subject: [PATCH 59/71] UnsubscribeAll Event (#175) * Added UnsubscribeAllEvent to states --- .../EventEngine/Subscribe/Events/SubscriptionEvents.cs | 4 +++- .../EventEngine/Subscribe/States/HandshakeFailedState.cs | 4 ++++ .../Subscribe/States/HandshakeReconnectingState.cs | 4 ++++ .../EventEngine/Subscribe/States/HandshakeStoppedState.cs | 4 ++++ .../EventEngine/Subscribe/States/HandshakingState.cs | 4 ++++ .../EventEngine/Subscribe/States/ReceiveFailedState.cs | 4 ++++ .../EventEngine/Subscribe/States/ReceiveReconnectingState.cs | 4 ++++ .../EventEngine/Subscribe/States/ReceiveStoppedState.cs | 4 ++++ .../PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs | 4 ++++ .../EventEngine/Subscribe/States/UnsubscribedState.cs | 3 +++ 10 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index 42739802d..90efa6b9a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; namespace PubnubApi.PubnubEventEngine.Subscribe.Events { - public class SubscriptionChangedEvent : Core.IEvent { + public class UnsubscribeAllEvent : Core.IEvent { + } + public class SubscriptionChangedEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index a62e27b18..0ce533158 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -14,6 +14,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index ae8fd3524..a9dff98e3 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -18,6 +18,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index f58100818..fc69db008 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -14,6 +14,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 7b2584c52..9d3b50e18 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -19,6 +19,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new States.HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index 6c7e4632c..f6fc39ca4 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -18,6 +18,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { Channels = subscriptionChanged.Channels, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 7d9bbaf9d..d0eac310f 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -21,6 +21,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { Channels = subscriptionChanged.Channels, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 06c6daded..bec729a2b 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -15,6 +15,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { Channels = subscriptionChanged.Channels, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index 428507ddb..d94a67d9f 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -20,6 +20,10 @@ public override TransitionResult Transition(IEvent e) { return e switch { + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() + { + }, + Events.ReceiveSuccessEvent receiveSuccess => new ReceivingState() { Channels = receiveSuccess.Channels, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 2a5affc92..12dd75edd 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -7,6 +7,9 @@ namespace PubnubApi.PubnubEventEngine.Subscribe.States { internal class UnsubscribedState : Core.State { + public override IEnumerable OnEntry { get; } = null; + public override IEnumerable OnExit { get; } = null; + public override TransitionResult Transition(Core.IEvent e) { return e switch From 6fa332c4bbc74776af199abf1b751cf3156b3e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= <106665140+MikeDobrzan@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:34:43 +0200 Subject: [PATCH 60/71] Eventengine/handshake handler (#181) * feat: simplified abstraction * feat: handshake effect handler * fix: initial retry implementation * handshake reconnect * fix: handshake handler * revert *.csproj changes * added required info in handshake reconnect invocation. * receive handler (#180) * receive handler * fix: background handlers * *reconnection configurations *refactor naming * passing configuration for reconnection * fix: transition values from receive reconnect * wip: receive messages * Revert "Merge remote-tracking branch 'origin/eventengine/handshake-handler' into eventengine/handshake-handler" This reverts commit be568f7a1b1127151de3e56960539e1019d92bbd, reversing changes made to 0da6c9a314f7dcfddf6edadb89d8f7d31f8490f1. * fix * ReceivingEffectHandler - ReceivingResponse * null check * SubscribeEventEngine - receiveHandler * *csproj file for RReceivingEffectHandler * EmitStatus effect handler - take1 * empty task * wip emit messages * wip emit messages * cleanup and unify convention for *emitters * missing changes emitmessage * Added publisher * emitmessages --------- Co-authored-by: Mohit Tejani Co-authored-by: Pandu Masabathula --- .../EndPoint/PubSub/SubscribeOperation2.cs | 26 +-- src/Api/PubnubApi/EventEngine/Common/Delay.cs | 60 +++++++ .../EventEngine/Core/EffectDispatcher.cs | 11 +- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 2 +- .../EventEngine/Core/EventEngineInterfaces.cs | 3 +- .../PubnubApi/EventEngine/Core/EventQueue.cs | 2 +- src/Api/PubnubApi/EventEngine/Core/Utils.cs | 2 +- .../Subscribe/Common/CommonSubscribeTypes.cs | 85 ++++++++++ .../Context/ReconnectionDelayUtil.cs | 30 ++++ .../Subscribe/Effects/EmitMessagesHandler.cs | 47 ++++++ .../Effects/EmitStatusEffectHandler.cs | 30 ++++ .../Effects/HandshakeEffectHandler.cs | 159 +++++++++++++----- .../Effects/ReceivingEffectHandler.cs | 120 +++++++++++++ .../Subscribe/Events/SubscriptionEvents.cs | 17 +- .../Invocations/SubscriptionInvocations.cs | 28 +-- .../Subscribe/States/HandshakeFailedState.cs | 6 +- .../States/HandshakeReconnectingState.cs | 11 +- .../Subscribe/States/HandshakeStoppedState.cs | 6 +- .../Subscribe/States/HandshakingState.cs | 8 +- .../Subscribe/States/ReceiveFailedState.cs | 7 +- .../States/ReceiveReconnectingState.cs | 7 +- .../Subscribe/States/ReceiveStoppedState.cs | 7 +- .../Subscribe/States/ReceivingState.cs | 7 +- .../Subscribe/States/UnsubscribedState.cs | 9 +- .../Subscribe/SubscribeEventEngine.cs | 17 +- src/Api/PubnubApi/Model/Consumer/PNStatus.cs | 10 ++ src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 7 + src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 7 + .../Features/grant-token.feature.cs | 6 +- .../Features/revoke-token.feature.cs | 4 +- 30 files changed, 618 insertions(+), 123 deletions(-) create mode 100644 src/Api/PubnubApi/EventEngine/Common/Delay.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 7e9511bf7..61b9d5c26 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -26,7 +26,7 @@ public class SubscribeOperation2: ISubscribeOperation private bool presenceSubscribeEnabled; private SubscribeManager2 manager; private Dictionary queryParam; - private EventEngine pnEventEngine; + private PubnubEventEngine.EventEngine pnEventEngine; private Pubnub PubnubInstance; public List SubscribeListenerList { @@ -104,18 +104,18 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j effectDispatcher.Register(EventType.ReceiveReconnectSuccess, receiveReconnectEffectHandler); effectDispatcher.Register(EventType.ReceiveReconnectGiveUp, receiveReconnectEffectHandler); - 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 }); - } + // 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 ReceivingEffect_ReceiveRequested(object sender, ReceiveRequestEventArgs e) 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 index babde75de..334366490 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace PubnubApi.PubnubEventEngine.Core { +namespace PubnubApi.EventEngine.Core { internal class EffectDispatcher { // assumes 1 instance of handler - capable of managing itself private readonly Dictionary effectInvocationHandlerMap = @@ -18,8 +18,13 @@ public async Task Dispatch(T invocation) where T : IEffectInvocation { if (invocation is IEffectCancelInvocation) { await effectInvocationHandlerMap[invocation.GetType()].Cancel(); - } else { - await ((IEffectHandler)effectInvocationHandlerMap[invocation.GetType()]).Run(invocation); + } else + { + var handler = ((IEffectHandler)effectInvocationHandlerMap[invocation.GetType()]); + if (handler.IsBackground(invocation)) + handler.Run(invocation).Start(); + else + await handler.Run(invocation); } } diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index f0315ac13..11b176c16 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using System.Collections.Generic; -namespace PubnubApi.PubnubEventEngine.Core { +namespace PubnubApi.EventEngine.Core { internal abstract class Engine { public EventQueue eventQueue = new EventQueue(); diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 4dbc203b9..16717dc36 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using System.Collections.Generic; -namespace PubnubApi.PubnubEventEngine.Core { +namespace PubnubApi.EventEngine.Core { /// /// Generic effect handler. @@ -17,6 +17,7 @@ internal interface IEffectHandler { /// Associated invocation internal interface IEffectHandler : IEffectHandler where T : IEffectInvocation { Task Run(T invocation); + bool IsBackground(T invocation); } /// diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index a0eb2fe64..a23e30d39 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading.Tasks; -namespace PubnubApi.PubnubEventEngine.Core +namespace PubnubApi.EventEngine.Core { internal class EventQueue { diff --git a/src/Api/PubnubApi/EventEngine/Core/Utils.cs b/src/Api/PubnubApi/EventEngine/Core/Utils.cs index f3edacb10..c7c4cbe04 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Utils.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using System.Collections.Generic; -namespace PubnubApi.PubnubEventEngine.Core +namespace PubnubApi.EventEngine.Core { internal static class Utils { 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..19157d716 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs @@ -0,0 +1,85 @@ +using Newtonsoft.Json; + +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; } + } +} \ No newline at end of file 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..07749610c --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs @@ -0,0 +1,30 @@ +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(PNReconnectionPolicy policy, int attempts, int maxAttempts) + { + if (policy == PNReconnectionPolicy.NONE) return false; + return maxAttempts < attempts; + } + } +} + 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..77c7e1eb8 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs @@ -0,0 +1,47 @@ +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 +{ + internal class EmitMessagesHandler : IEffectHandler + { + private readonly System.Action> messageEmitterFunction; + private readonly Pubnub pubnubInstance; + + public EmitMessagesHandler(Pubnub pubnubInstance, + System.Action> messageEmitterFunction) + { + this.messageEmitterFunction = messageEmitterFunction; + this.pubnubInstance = pubnubInstance; + } + + public async 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 + }); + + foreach (var message in processedMessages) + { + messageEmitterFunction(pubnubInstance, message); + } + } + + public bool IsBackground(EmitMessagesInvocation invocation) => false; + + public 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..e52ecae06 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; + +namespace PubnubApi.EventEngine.Subscribe.Effects +{ + public class EmitStatusEffectHandler: Core.IEffectHandler + { + private readonly Action statusEmitterFunction; + private readonly Pubnub pubnubInstance; + + public EmitStatusEffectHandler(Pubnub pn, Action statusEmitter) + { + this.statusEmitterFunction = statusEmitter; + this.pubnubInstance = pn; + } + + public Task Cancel() => Utils.EmptyTask; + + bool IEffectHandler.IsBackground(EmitStatusInvocation invocation) => false; + + Task IEffectHandler.Run(EmitStatusInvocation invocation) + { + this.statusEmitterFunction(this.pubnubInstance, invocation.Status); + return Utils.EmptyTask; + } + } +} + diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index 72a15f8ce..f85f995d0 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -1,48 +1,121 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using PubnubApi.EndPoint; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; - -namespace PubnubApi.PubnubEventEngine.Subscribe.Effects { - internal class HandshakeEffectHandler : Core.IEffectHandler { - private SubscribeManager2 manager; - private EventQueue eventQueue; - - public HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { - this.manager = manager; - this.eventQueue = eventQueue; - } - - public async Task Run(HandshakeInvocation invocation) { - // TODO fix this, probably wrong :) - var resp = await manager.HandshakeRequest( - PNOperationType.PNSubscribeOperation, - invocation.Channels.ToArray(), - invocation.ChannelGroups.ToArray(), - null, - null, - invocation.InitialSubscribeQueryParams, - invocation.ExternalQueryParams - ); - - if (!resp.Item2.Error) { - // TODO move deserialization outside - // TODO does this need more error checking? - var handshakeResponse = JsonConvert.DeserializeObject(resp.Item1); - var c = new SubscriptionCursor() { - Region = handshakeResponse.Timetoken.Region, - Timetoken = handshakeResponse.Timetoken.Timestamp - }; - - eventQueue.Enqueue(new Events.HandshakeSuccessEvent() {Cursor = c}); - } - } - - public async Task Cancel() { - manager.HandshakeRequestCancellation(); - } - } +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 +{ + internal class HandshakeEffectHandler : + Core.IEffectHandler, + Core.IEffectHandler + { + private SubscribeManager2 manager; + private EventQueue eventQueue; + + private Delay retryDelay = new Delay(0); + + public HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) + { + this.manager = manager; + this.eventQueue = eventQueue; + } + + public async Task Run(HandshakeReconnectInvocation invocation) + { + if (!ReconnectionDelayUtil.shouldRetry(invocation.Policy, invocation.AttemptedRetries, invocation.MaxConnectionRetry)) + { + eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); + } + else + { + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.Policy, invocation.AttemptedRetries)); + await retryDelay.Start(); + if (!retryDelay.Cancelled) + await Run((HandshakeInvocation)invocation); + } + } + + public bool IsBackground(HandshakeReconnectInvocation invocation) + { + return true; + } + + public async Task Run(HandshakeInvocation invocation) + { + var response = await MakeHandshakeRequest(invocation); + + 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 = response.Item1, 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 = response.Item1, Status = response.Item2 }); + break; + + } + } + + public bool IsBackground(HandshakeInvocation invocation) + { + return false; + } + + + private async Task> MakeHandshakeRequest(HandshakeInvocation invocation) + { + var resp = await manager.HandshakeRequest( + PNOperationType.PNSubscribeOperation, + invocation.Channels.ToArray(), + invocation.ChannelGroups.ToArray(), + null, + null, + invocation.InitialSubscribeQueryParams, + invocation.ExternalQueryParams + ); + + try + { + var handshakeResponse = JsonConvert.DeserializeObject(resp.Item1); + var c = new SubscriptionCursor() + { + Region = handshakeResponse.Timetoken.Region, + Timetoken = handshakeResponse.Timetoken.Timestamp + }; + return new System.Tuple(c, resp.Item2); + } + catch (Exception e) + { + return new Tuple(null, new PNStatus(e, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, invocation.Channels, invocation.ChannelGroups)); + } + } + + public 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..d80cf0b3a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -0,0 +1,120 @@ +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 +{ + internal class ReceivingEffectHandler: + Core.IEffectHandler, + Core.IEffectHandler + { + private SubscribeManager2 manager; + private EventQueue eventQueue; + + private Delay retryDelay = new Delay(0); + + public ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) + { + this.manager = manager; + this.eventQueue = eventQueue; + } + + public Task Run(ReceiveReconnectInvocation invocation) + { + if (!ReconnectionDelayUtil.shouldRetry(invocation.Policy, invocation.AttemptedRetries, invocation.MaxConnectionRetry)) + { + eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); + } + else + { + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.Policy, invocation.AttemptedRetries)); + // Run in the background + retryDelay.Start().ContinueWith((_) => this.Run((ReceiveMessagesInvocation)invocation)); + } + + return Utils.EmptyTask; + } + + public bool IsBackground(ReceiveReconnectInvocation invocation) + { + return true; + } + + public 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 bool IsBackground(ReceiveMessagesInvocation invocation) + { + return true; + } + + private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) + { + var resp = await manager.ReceiveRequest( + PNOperationType.PNSubscribeOperation, + invocation.Channels.ToArray(), + invocation.ChannelGroups.ToArray(), + invocation.Cursor.Timetoken.Value, + invocation.Cursor.Region.Value, + invocation.InitialSubscribeQueryParams, + invocation.ExternalQueryParams + ); + + try + { + //TODO: get ReceivingResponse from manager.ReceiveRequest + var receiveResponse = JsonConvert.DeserializeObject>(resp.Item1); + return new System.Tuple, PNStatus>(receiveResponse, resp.Item2); + } + catch (Exception e) + { + return new Tuple, PNStatus>(null, new PNStatus(e, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, invocation.Channels, invocation.ChannelGroups)); + } + } + + public 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 index 90efa6b9a..605826a14 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.PubnubEventEngine.Subscribe.Events { - public class UnsubscribeAllEvent : Core.IEvent { - } - public class SubscriptionChangedEvent : Core.IEvent { +namespace PubnubApi.EventEngine.Subscribe.Events { + public class UnsubscribeAllEvent : Core.IEvent { + } + + public class SubscriptionChangedEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; } @@ -21,6 +23,7 @@ public class HandshakeSuccessEvent : Core.IEvent { public class HandshakeFailureEvent : Core.IEvent { public PNStatus Status; + public int AttemptedRetries; } public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { @@ -29,9 +32,9 @@ public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { public class HandshakeReconnectFailureEvent : HandshakeFailureEvent { - public PNStatus Status; } + // Do we have this in system description ? public class HandshakeReconnectRetryEvent : Core.IEvent { } @@ -42,13 +45,15 @@ public class HandshakeReconnectGiveUpEvent : Core.IEvent { public class ReceiveSuccessEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; - public List> Messages; + 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 { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs index 7fc22eceb..d585580f9 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.PubnubEventEngine.Subscribe.Invocations { +namespace PubnubApi.EventEngine.Subscribe.Invocations { internal class EmitMessagesInvocation : Core.IEffectInvocation { - public IEnumerable> Messages; + public ReceivingResponse Messages; - public EmitMessagesInvocation(IEnumerable> messages) + public EmitMessagesInvocation(ReceivingResponse messages) { this.Messages = messages; } @@ -44,25 +45,28 @@ internal class ReceiveMessagesInvocation : Core.IEffectInvocation public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public Dictionary InitialSubscribeQueryParams = new Dictionary(); + public Dictionary ExternalQueryParams = new Dictionary(); } internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } internal class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } - internal class HandshakeReconnectInvocation: Core.IEffectInvocation - { - public IEnumerable Channels; - public IEnumerable ChannelGroups; + internal class HandshakeReconnectInvocation: HandshakeInvocation + { + public int AttemptedRetries; + public int MaxConnectionRetry; + public PNReconnectionPolicy Policy; } internal class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } - internal class ReceiveReconnectInvocation: Core.IEffectInvocation + internal class ReceiveReconnectInvocation: ReceiveMessagesInvocation { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; + public int AttemptedRetries; + public int MaxConnectionRetry; + public PNReconnectionPolicy Policy; } internal class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index 0ce533158..a09f05a6e 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class HandshakeFailedState : Core.State { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index a9dff98e3..74f4848e4 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -1,17 +1,20 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class HandshakeReconnectingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; + public PNReconnectionPolicy RetryPolicy; + public int MaxConnectionRetry; + public int AttemptedRetries; public override IEnumerable OnEntry => new HandshakeReconnectInvocation() - { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); + { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Policy = this.RetryPolicy, MaxConnectionRetry = this.MaxConnectionRetry, AttemptedRetries = this.AttemptedRetries }.AsArray(); public override IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); public override TransitionResult Transition(IEvent e) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index fc69db008..95c0cd190 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class HandshakeStoppedState : Core.State { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 9d3b50e18..14b610f8b 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class HandshakingState : Core.State { @@ -35,7 +35,7 @@ public override TransitionResult Transition(IEvent e) Cursor = subscriptionRestored.Cursor }, - Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeFailedState() + Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeReconnectingState() { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.With(new EmitStatusInvocation(handshakeFailure.Status)), diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index f6fc39ca4..69139e66f 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class ReceiveFailedState : Core.State { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index d0eac310f..60c7f1f84 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class ReceiveReconnectingState : Core.State { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index bec729a2b..9407f0ca2 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class ReceiveStoppedState : Core.State { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index d94a67d9f..7b9991ef8 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class ReceivingState : Core.State { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 12dd75edd..f1e9fc683 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; -using PubnubApi.PubnubEventEngine.Core; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe.States +namespace PubnubApi.EventEngine.Subscribe.States { internal class UnsubscribedState : Core.State { - public override IEnumerable OnEntry { get; } = null; - public override IEnumerable OnExit { get; } = null; - public override TransitionResult Transition(Core.IEvent e) { return e switch diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs index f71bd366f..862fb5819 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -1,10 +1,11 @@ using PubnubApi.EndPoint; -using PubnubApi.PubnubEventEngine.Subscribe.Effects; -using PubnubApi.PubnubEventEngine.Subscribe.Invocations; -using PubnubApi.PubnubEventEngine.Subscribe.States; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.States; +using PubnubApi.EventEngine.Subscribe.Effects; +using PubnubApi.EventEngine.Subscribe.Invocations; -namespace PubnubApi.PubnubEventEngine.Subscribe { - internal class SubscribeEventEngine : PubnubEventEngine.Core.Engine { +namespace PubnubApi.EventEngine.Subscribe { + internal class SubscribeEventEngine : Engine { private SubscribeManager2 subscribeManager; public SubscribeEventEngine(SubscribeManager2 subscribeManager) { @@ -13,8 +14,14 @@ public SubscribeEventEngine(SubscribeManager2 subscribeManager) { // initialize the handler, pass dependencies var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, eventQueue); 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); + currentState = new UnsubscribedState(); } } diff --git a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs index 5abb9de27..4e3e3a5ff 100644 --- a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs +++ b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs @@ -14,6 +14,16 @@ public class PNStatus public PNStatus() { } + public PNStatus(Exception e, PNOperationType operationType, PNStatusCategory category, IEnumerable affectedChannels = null, IEnumerable affectedChannelGroups = null) + { + this.Error = true; + this.Operation = operationType; + this.ErrorData = new PNErrorData(e.Message, e); + this.AffectedChannels = affectedChannels?.ToList(); + this.AffectedChannelGroups = affectedChannelGroups?.ToList(); + this.Category = category; + } + internal PNStatus(object endpointOperation) { this.savedEndpointOperation = endpointOperation; diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 4527aad73..356a8f73b 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -213,6 +213,7 @@ Enum\ResponseType.cs + @@ -228,7 +229,10 @@ + + + @@ -917,9 +921,12 @@ + + + diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 0b0b9c5d0..00800be1c 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -330,6 +330,7 @@ Enum\ResponseType.cs + @@ -345,7 +346,10 @@ + + + @@ -726,9 +730,12 @@ + + + diff --git a/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs index a1aaaf549..c8bb56301 100644 --- a/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs @@ -37,9 +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, featureTags); + 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); } diff --git a/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs index d5a85c045..f98f833b4 100644 --- a/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs @@ -39,8 +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, featureTags); + 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); } From 4290809854fac9c78b6076cfd5bb6b95a97d29b8 Mon Sep 17 00:00:00 2001 From: Mohit Tejani Date: Tue, 25 Jul 2023 18:53:06 +0530 Subject: [PATCH 61/71] effect handler cleanup emit status --- .../Subscribe/Effects/EmitStatusEffectHandler.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs index e52ecae06..5e1e47795 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - public class EmitStatusEffectHandler: Core.IEffectHandler + internal class EmitStatusEffectHandler: IEffectHandler { private readonly Action statusEmitterFunction; private readonly Pubnub pubnubInstance; @@ -18,13 +18,11 @@ public EmitStatusEffectHandler(Pubnub pn, Action statusEmitter public Task Cancel() => Utils.EmptyTask; - bool IEffectHandler.IsBackground(EmitStatusInvocation invocation) => false; + public bool IsBackground(EmitStatusInvocation invocation) => false; - Task IEffectHandler.Run(EmitStatusInvocation invocation) + public async Task Run(EmitStatusInvocation invocation) { this.statusEmitterFunction(this.pubnubInstance, invocation.Status); - return Utils.EmptyTask; } } -} - +} \ No newline at end of file From c15264859adda3de8d5b79eebb8dfb28db38e8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Thu, 27 Jul 2023 15:23:44 +0200 Subject: [PATCH 62/71] wip: switch to public --- .../EventEngine/Core/EffectDispatcher.cs | 2 +- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 2 +- .../EventEngine/Core/EventEngineInterfaces.cs | 12 +++++------ .../PubnubApi/EventEngine/Core/EventQueue.cs | 2 +- src/Api/PubnubApi/EventEngine/Core/Utils.cs | 2 +- .../Subscribe/Effects/EmitMessagesHandler.cs | 2 +- .../Effects/EmitStatusEffectHandler.cs | 2 +- .../Effects/HandshakeEffectHandler.cs | 4 ++-- .../Effects/ReceivingEffectHandler.cs | 4 ++-- .../Invocations/SubscriptionInvocations.cs | 20 +++++++++---------- .../Subscribe/States/HandshakeFailedState.cs | 2 +- .../States/HandshakeReconnectingState.cs | 2 +- .../Subscribe/States/HandshakeStoppedState.cs | 2 +- .../Subscribe/States/HandshakingState.cs | 2 +- .../Subscribe/States/ReceiveFailedState.cs | 2 +- .../States/ReceiveReconnectingState.cs | 2 +- .../Subscribe/States/ReceiveStoppedState.cs | 2 +- .../Subscribe/States/ReceivingState.cs | 2 +- .../Subscribe/States/UnsubscribedState.cs | 2 +- .../Subscribe/SubscribeEventEngine.cs | 4 ++-- 20 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs index 334366490..d0f3b6488 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace PubnubApi.EventEngine.Core { - internal class EffectDispatcher { + public class EffectDispatcher { // assumes 1 instance of handler - capable of managing itself private readonly Dictionary effectInvocationHandlerMap = new Dictionary(); diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 11b176c16..7722a4d89 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; namespace PubnubApi.EventEngine.Core { - internal abstract class Engine { + public abstract class Engine { public EventQueue eventQueue = new EventQueue(); protected EffectDispatcher dispatcher = new EffectDispatcher(); diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 16717dc36..2d5aad22f 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -7,7 +7,7 @@ namespace PubnubApi.EventEngine.Core { /// /// Generic effect handler. /// - internal interface IEffectHandler { + public interface IEffectHandler { Task Cancel(); } @@ -15,7 +15,7 @@ internal interface IEffectHandler { /// Handler (implementation) for a given invocation. The invocation represents the input arguments of a handler. /// /// Associated invocation - internal interface IEffectHandler : IEffectHandler where T : IEffectInvocation { + public interface IEffectHandler : IEffectHandler where T : IEffectInvocation { Task Run(T invocation); bool IsBackground(T invocation); } @@ -23,16 +23,16 @@ internal interface IEffectHandler : IEffectHandler where T : IEffectInvoca /// /// An effect invocation. It represents calling Run() on a registered effect handler - calling it is orchestrated by the dispatcher. /// - internal interface IEffectInvocation { } + public interface IEffectInvocation { } /// /// A cancel effect invocation. It represents calling Cancel() on a registered effect handler - calling it is orchestrated by the dispatcher. /// - internal interface IEffectCancelInvocation : IEffectInvocation { } + public interface IEffectCancelInvocation : IEffectInvocation { } - internal interface IEvent { }; + public interface IEvent { }; - internal abstract class State + public abstract class State { public virtual IEnumerable OnEntry { get; } = null; public virtual IEnumerable OnExit { get; } = null; diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index a23e30d39..8d3c9df88 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -4,7 +4,7 @@ namespace PubnubApi.EventEngine.Core { - internal class EventQueue + public class EventQueue { private volatile Queue eventQueue = new Queue(); private object lockObj = new object(); diff --git a/src/Api/PubnubApi/EventEngine/Core/Utils.cs b/src/Api/PubnubApi/EventEngine/Core/Utils.cs index c7c4cbe04..8657d6fa7 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Utils.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -19,7 +19,7 @@ internal static IEffectInvocation[] AsArray(this IEffectInvocation invocation) } } - internal class TransitionResult + public class TransitionResult { public State State => tuple.Item1; public IEnumerable Invocations => tuple.Item2; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs index 77c7e1eb8..9ace826a1 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs @@ -7,7 +7,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - internal class EmitMessagesHandler : IEffectHandler + public class EmitMessagesHandler : IEffectHandler { private readonly System.Action> messageEmitterFunction; private readonly Pubnub pubnubInstance; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs index 5e1e47795..366730ed5 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - internal class EmitStatusEffectHandler: IEffectHandler + public class EmitStatusEffectHandler: IEffectHandler { private readonly Action statusEmitterFunction; private readonly Pubnub pubnubInstance; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index f85f995d0..191682897 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -14,7 +14,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - internal class HandshakeEffectHandler : + public class HandshakeEffectHandler : Core.IEffectHandler, Core.IEffectHandler { @@ -23,7 +23,7 @@ internal class HandshakeEffectHandler : private Delay retryDelay = new Delay(0); - public HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) + internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { this.manager = manager; this.eventQueue = eventQueue; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs index d80cf0b3a..766256f7d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -14,7 +14,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - internal class ReceivingEffectHandler: + public class ReceivingEffectHandler: Core.IEffectHandler, Core.IEffectHandler { @@ -23,7 +23,7 @@ internal class ReceivingEffectHandler: private Delay retryDelay = new Delay(0); - public ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) + internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { this.manager = manager; this.eventQueue = eventQueue; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs index d585580f9..285a11f62 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -3,7 +3,7 @@ using PubnubApi.EventEngine.Subscribe.Common; namespace PubnubApi.EventEngine.Subscribe.Invocations { - internal class EmitMessagesInvocation : Core.IEffectInvocation { + public class EmitMessagesInvocation : Core.IEffectInvocation { public ReceivingResponse Messages; public EmitMessagesInvocation(ReceivingResponse messages) @@ -12,7 +12,7 @@ public EmitMessagesInvocation(ReceivingResponse messages) } } - internal class EmitStatusInvocation : Core.IEffectInvocation { + public class EmitStatusInvocation : Core.IEffectInvocation { // TODO merge status variables into one? public PNStatusCategory StatusCategory; public PNStatus Status; @@ -32,7 +32,7 @@ public EmitStatusInvocation(PNStatusCategory category) } } - internal class HandshakeInvocation : Core.IEffectInvocation { + public class HandshakeInvocation : Core.IEffectInvocation { public IEnumerable Channels; public IEnumerable ChannelGroups; // TODO if we need these, figure out how to pass them. @@ -40,7 +40,7 @@ internal class HandshakeInvocation : Core.IEffectInvocation { public Dictionary ExternalQueryParams = new Dictionary(); } - internal class ReceiveMessagesInvocation : Core.IEffectInvocation + public class ReceiveMessagesInvocation : Core.IEffectInvocation { public IEnumerable Channels; public IEnumerable ChannelGroups; @@ -49,25 +49,25 @@ internal class ReceiveMessagesInvocation : Core.IEffectInvocation public Dictionary ExternalQueryParams = new Dictionary(); } - internal class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } + public class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } - internal class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } + public class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } - internal class HandshakeReconnectInvocation: HandshakeInvocation + public class HandshakeReconnectInvocation: HandshakeInvocation { public int AttemptedRetries; public int MaxConnectionRetry; public PNReconnectionPolicy Policy; } - internal class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } + public class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } - internal class ReceiveReconnectInvocation: ReceiveMessagesInvocation + public class ReceiveReconnectInvocation: ReceiveMessagesInvocation { public int AttemptedRetries; public int MaxConnectionRetry; public PNReconnectionPolicy Policy; } - internal class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } + 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 index a09f05a6e..f26230490 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class HandshakeFailedState : Core.State + public class HandshakeFailedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index 74f4848e4..020e08751 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class HandshakeReconnectingState : Core.State + public class HandshakeReconnectingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 95c0cd190..259c7dbb1 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class HandshakeStoppedState : Core.State + public class HandshakeStoppedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 14b610f8b..6f92a96cc 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class HandshakingState : Core.State + public class HandshakingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index 69139e66f..dcbeb3bea 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -6,7 +6,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class ReceiveFailedState : Core.State + public class ReceiveFailedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 60c7f1f84..1fe8f71a7 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -6,7 +6,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class ReceiveReconnectingState : Core.State + public class ReceiveReconnectingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 9407f0ca2..25fd85ca8 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -6,7 +6,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class ReceiveStoppedState : Core.State + public class ReceiveStoppedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index 7b9991ef8..ad2deee32 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -6,7 +6,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class ReceivingState : Core.State + public class ReceivingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index f1e9fc683..944aac1b2 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - internal class UnsubscribedState : Core.State + public class UnsubscribedState : Core.State { public override TransitionResult Transition(Core.IEvent e) { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs index 862fb5819..84a28faa0 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -5,10 +5,10 @@ using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe { - internal class SubscribeEventEngine : Engine { + public class SubscribeEventEngine : Engine { private SubscribeManager2 subscribeManager; - public SubscribeEventEngine(SubscribeManager2 subscribeManager) { + internal SubscribeEventEngine(SubscribeManager2 subscribeManager) { this.subscribeManager = subscribeManager; // initialize the handler, pass dependencies From 9b715394393c96717055b603a6f50a443fc0fb7b Mon Sep 17 00:00:00 2001 From: Mohit Tejani Date: Mon, 31 Jul 2023 09:40:20 +0530 Subject: [PATCH 63/71] restore overridden changes from 83d3929 --- .../Context/ReconnectionConfiguration.cs | 16 +++ .../Context/ReconnectionDelayUtil.cs | 6 +- .../Effects/HandshakeEffectHandler.cs | 4 +- .../Effects/ReceivingEffectHandler.cs | 4 +- .../Invocations/SubscriptionInvocations.cs | 7 +- .../Subscribe/States/HandshakeFailedState.cs | 18 ++- .../States/HandshakeReconnectingState.cs | 117 +++++++++++------- .../Subscribe/States/HandshakeStoppedState.cs | 18 ++- .../Subscribe/States/HandshakingState.cs | 32 +++-- .../Subscribe/States/ReceiveFailedState.cs | 22 ++-- .../States/ReceiveReconnectingState.cs | 39 ++++-- .../Subscribe/States/ReceiveStoppedState.cs | 12 +- .../Subscribe/States/ReceivingState.cs | 25 ++-- .../Subscribe/States/UnsubscribedState.cs | 10 +- .../Subscribe/SubscribeEventEngine.cs | 1 + 15 files changed, 222 insertions(+), 109 deletions(-) create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs 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..7346345d9 --- /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; } = PNReconnectionPolicy.NONE; + public int MaximumReconnectionRetries; + + 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 index 07749610c..fb651aaae 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs @@ -20,10 +20,10 @@ public static int CalculateDelay(PNReconnectionPolicy policy, int attempts) } - public static bool shouldRetry(PNReconnectionPolicy policy, int attempts, int maxAttempts) + public static bool shouldRetry(ReconnectionConfiguration reconnectionConfiguration, int attemptedRetries) { - if (policy == PNReconnectionPolicy.NONE) return false; - return maxAttempts < attempts; + if (reconnectionConfiguration.ReconnectionPolicy == PNReconnectionPolicy.NONE) return false; + return reconnectionConfiguration.MaximumReconnectionRetries < attemptedRetries; } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index 191682897..1c214c5f3 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -31,13 +31,13 @@ internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue public async Task Run(HandshakeReconnectInvocation invocation) { - if (!ReconnectionDelayUtil.shouldRetry(invocation.Policy, invocation.AttemptedRetries, invocation.MaxConnectionRetry)) + if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) { eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); } else { - retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.Policy, invocation.AttemptedRetries)); + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); await retryDelay.Start(); if (!retryDelay.Cancelled) await Run((HandshakeInvocation)invocation); diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs index 766256f7d..98e2582d4 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -31,13 +31,13 @@ internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue public Task Run(ReceiveReconnectInvocation invocation) { - if (!ReconnectionDelayUtil.shouldRetry(invocation.Policy, invocation.AttemptedRetries, invocation.MaxConnectionRetry)) + if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) { eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); } else { - retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.Policy, invocation.AttemptedRetries)); + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); // Run in the background retryDelay.Start().ContinueWith((_) => this.Run((ReceiveMessagesInvocation)invocation)); } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs index 285a11f62..fe474e164 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -1,6 +1,7 @@ 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 { @@ -55,18 +56,16 @@ public class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancel public class HandshakeReconnectInvocation: HandshakeInvocation { + public ReconnectionConfiguration ReconnectionConfiguration; public int AttemptedRetries; - public int MaxConnectionRetry; - public PNReconnectionPolicy Policy; } public class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } public class ReceiveReconnectInvocation: ReceiveMessagesInvocation { + public ReconnectionConfiguration ReconnectionConfiguration; public int AttemptedRetries; - public int MaxConnectionRetry; - public PNReconnectionPolicy Policy; } public class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index f26230490..c5c75c2f9 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -9,30 +10,37 @@ public class HandshakeFailedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; + public ReconnectionConfiguration ReconnectionConfiguration; - public override TransitionResult Transition(IEvent e) + 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, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.ReconnectEvent reconnect => new HandshakingState() { - Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index 020e08751..c1efbdf04 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -1,64 +1,85 @@ using System; using System.Collections.Generic; using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States { - public class HandshakeReconnectingState : Core.State - { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public PNReconnectionPolicy RetryPolicy; - public int MaxConnectionRetry; - public int AttemptedRetries; + public class HandshakeReconnectingState : Core.State + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public ReconnectionConfiguration ReconnectionConfiguration; + public int AttemptedRetries; - public override IEnumerable OnEntry => new HandshakeReconnectInvocation() - { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Policy = this.RetryPolicy, MaxConnectionRetry = this.MaxConnectionRetry, AttemptedRetries = this.AttemptedRetries }.AsArray(); - public override IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); + 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() - { - }, + 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, - }, + 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 - }, + 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 - }.With( - new EmitStatusInvocation(handshakeReconnectGiveUp.Status) - ), + Events.HandshakeReconnectGiveUpEvent handshakeReconnectGiveUp => new HandshakeFailedState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With( + new EmitStatusInvocation(handshakeReconnectGiveUp.Status) + ), - Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess => new ReceivingState() - { - Channels = this.Channels, - ChannelGroups = this.ChannelGroups, - Cursor = handshakeReconnectSuccess.Cursor - }.With(new EmitStatusInvocation(handshakeReconnectSuccess.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.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() - { - Channels = subscriptionRestored.Channels, - ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, + Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess => new ReceivingState() + { + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = handshakeReconnectSuccess.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(handshakeReconnectSuccess.Status)), - _ => null - }; - } - } + Events.SubscriptionRestoredEvent subscriptionRestored => new 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/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 259c7dbb1..67c7ce638 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -9,30 +10,37 @@ public class HandshakeStoppedState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; - - public override TransitionResult Transition(IEvent e) + public ReconnectionConfiguration ReconnectionConfiguration; + + 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, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.ReconnectEvent reconnect => new HandshakingState() { - Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, + Channels = reconnect.Channels, + ChannelGroups = reconnect.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 6f92a96cc..248f39c76 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -9,9 +10,12 @@ public class HandshakingState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; + public ReconnectionConfiguration ReconnectionConfiguration; - public override IEnumerable OnEntry => new HandshakeInvocation() - { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); + + public override IEnumerable OnEntry => new HandshakeInvocation() + { Channels = this.Channels, + ChannelGroups = this.ChannelGroups }.AsArray(); public override IEnumerable OnExit { get; } = new CancelHandshakeInvocation().AsArray(); @@ -21,34 +25,46 @@ public override TransitionResult Transition(IEvent e) { Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionChangedEvent subscriptionChanged => new States.HandshakingState() { - Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeReconnectingState() { - Channels = this.Channels, ChannelGroups = this.ChannelGroups + 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, + 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 - }.With(new EmitStatusInvocation(success.Status)), + Channels = this.Channels, + ChannelGroups = this.ChannelGroups, + Cursor = success.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(success.Status)), _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index dcbeb3bea..abe34d23e 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -3,6 +3,7 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; namespace PubnubApi.EventEngine.Subscribe.States { @@ -11,8 +12,9 @@ public class ReceiveFailedState : Core.State public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public ReconnectionConfiguration ReconnectionConfiguration; - public IEnumerable OnEntry { get; } + public IEnumerable OnEntry { get; } public IEnumerable OnExit { get; } public override TransitionResult Transition(IEvent e) @@ -21,28 +23,32 @@ public override TransitionResult Transition(IEvent e) { Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { - }, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = this.Cursor - }, + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, Events.ReconnectEvent reconnect => new ReceivingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, - Cursor = reconnect.Cursor - }, + Cursor = reconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor - }, + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 1fe8f71a7..2501c2cc9 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -3,6 +3,7 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; namespace PubnubApi.EventEngine.Subscribe.States { @@ -11,9 +12,17 @@ public class ReceiveReconnectingState : Core.State 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 }.AsArray(); + 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(); @@ -24,49 +33,57 @@ public override TransitionResult Transition(IEvent e) { Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { - }, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = this.Cursor + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.DisconnectEvent disconnect => new ReceiveStoppedState() { Channels = disconnect.Channels, ChannelGroups = disconnect.ChannelGroups, - Cursor = disconnect.Cursor + 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 + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.ReceiveReconnectSuccessEvent receiveReconnectSuccess => new ReceivingState() { Channels = receiveReconnectSuccess.Channels, ChannelGroups = receiveReconnectSuccess.ChannelGroups, - Cursor = receiveReconnectSuccess.Cursor + 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 - }.With(new EmitStatusInvocation(receiveReconnectFailure.Status)), + 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 - }.With(new EmitStatusInvocation(receiveReconnectGiveUp.Status)), + Cursor = receiveReconnectGiveUp.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }.With(new EmitStatusInvocation(receiveReconnectGiveUp.Status)), _ => null }; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 25fd85ca8..c082f5242 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -3,6 +3,7 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; namespace PubnubApi.EventEngine.Subscribe.States { @@ -11,13 +12,15 @@ public class ReceiveStoppedState : Core.State public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public ReconnectionConfiguration ReconnectionConfiguration; - public override TransitionResult Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() @@ -25,20 +28,23 @@ public override TransitionResult Transition(IEvent e) Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.ReconnectEvent reconnect => new ReceivingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, - Cursor = reconnect.Cursor + Cursor = reconnect.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index ad2deee32..dfb7c205b 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -3,6 +3,7 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.EventEngine.Subscribe.Context; namespace PubnubApi.EventEngine.Subscribe.States { @@ -11,9 +12,10 @@ public class ReceivingState : Core.State public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public ReconnectionConfiguration ReconnectionConfiguration; - public override IEnumerable OnEntry => new ReceiveMessagesInvocation() - { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); + public override IEnumerable OnEntry => new ReceiveMessagesInvocation() + { Channels = this.Channels,ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); public override IEnumerable OnExit { get; } = new CancelReceiveMessagesInvocation().AsArray(); @@ -23,13 +25,15 @@ public override TransitionResult Transition(IEvent e) { Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { - }, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, Events.ReceiveSuccessEvent receiveSuccess => new ReceivingState() { Channels = receiveSuccess.Channels, ChannelGroups = receiveSuccess.ChannelGroups, - Cursor = receiveSuccess.Cursor + Cursor = receiveSuccess.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }.With( new EmitMessagesInvocation(receiveSuccess.Messages), new EmitStatusInvocation(receiveSuccess.Status) @@ -39,28 +43,33 @@ public override TransitionResult Transition(IEvent e) { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = this.Cursor + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.DisconnectEvent disconnect => new ReceiveStoppedState() { Channels = this.Channels, ChannelGroups = this.ChannelGroups, - Cursor = this.Cursor + 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 + Cursor = this.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration, + AttemptedRetries = 0 }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 944aac1b2..cfa44f6ad 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -1,26 +1,32 @@ using System; using System.Collections.Generic; using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States { public class UnsubscribedState : Core.State { + public ReconnectionConfiguration ReconnectionConfiguration; + public override TransitionResult Transition(Core.IEvent e) { return e switch { Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { - Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = subscriptionChanged.Channels, + ChannelGroups = subscriptionChanged.ChannelGroups, + ReconnectionConfiguration = this.ReconnectionConfiguration }, Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs index 84a28faa0..3a3c1de25 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -22,6 +22,7 @@ internal SubscribeEventEngine(SubscribeManager2 subscribeManager) { dispatcher.Register(receiveHandler); dispatcher.Register(receiveHandler); + // TODO: ReconnectionConfiguration currentState = new UnsubscribedState(); } } From 796b4b358071a2a296643d074c130e112f288fef Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Mon, 31 Jul 2023 12:21:15 +0530 Subject: [PATCH 64/71] csproj ReconnectionConfiguration to PCL/UWP --- src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 1 + src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 356a8f73b..bc14ab3e8 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -230,6 +230,7 @@ + diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 00800be1c..59b40e332 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -347,6 +347,7 @@ + From 0fee4974c97d53ea7c20b077f9fd610ec7c881ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Mon, 31 Jul 2023 10:49:45 +0200 Subject: [PATCH 65/71] wip: base class for states --- .../Subscribe/Common/CommonSubscribeTypes.cs | 9 +++++++++ .../Subscribe/States/HandshakeFailedState.cs | 9 +++------ .../Subscribe/States/HandshakeReconnectingState.cs | 6 ++---- .../Subscribe/States/HandshakeStoppedState.cs | 9 +++------ .../EventEngine/Subscribe/States/HandshakingState.cs | 10 +++------- .../EventEngine/Subscribe/States/ReceiveFailedState.cs | 9 +++------ .../Subscribe/States/ReceiveReconnectingState.cs | 2 +- .../Subscribe/States/ReceiveStoppedState.cs | 7 ++----- .../EventEngine/Subscribe/States/ReceivingState.cs | 7 ++----- .../EventEngine/Subscribe/States/UnsubscribedState.cs | 5 ++--- 10 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs index 19157d716..c3826ca42 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs @@ -1,4 +1,6 @@ using Newtonsoft.Json; +using System.Collections.Generic; +using PubnubApi.EventEngine.Subscribe.Context; namespace PubnubApi.EventEngine.Subscribe.Common { @@ -82,4 +84,11 @@ public class Message [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/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index c5c75c2f9..9ef3286d1 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -1,18 +1,15 @@ 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 : Core.State + public class HandshakeFailedState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public ReconnectionConfiguration ReconnectionConfiguration; - - public override TransitionResult Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index c1efbdf04..d84c4ccbc 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -1,16 +1,14 @@ 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 : Core.State + public class HandshakeReconnectingState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public ReconnectionConfiguration ReconnectionConfiguration; public int AttemptedRetries; public override IEnumerable OnEntry => new HandshakeReconnectInvocation() diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 67c7ce638..55618c04d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -1,18 +1,15 @@ 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 : Core.State + public class HandshakeStoppedState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public ReconnectionConfiguration ReconnectionConfiguration; - - public override TransitionResult Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 248f39c76..7272f73e7 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -1,19 +1,15 @@ 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 : Core.State + public class HandshakingState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public ReconnectionConfiguration ReconnectionConfiguration; - - - public override IEnumerable OnEntry => new HandshakeInvocation() + public override IEnumerable OnEntry => new HandshakeInvocation() { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index abe34d23e..3edcfd641 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -7,15 +7,12 @@ namespace PubnubApi.EventEngine.Subscribe.States { - public class ReceiveFailedState : Core.State + public class ReceiveFailedState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - public ReconnectionConfiguration ReconnectionConfiguration; - public IEnumerable OnEntry { get; } - public IEnumerable OnExit { get; } + public override IEnumerable OnEntry { get; } + public override IEnumerable OnExit { get; } public override TransitionResult Transition(IEvent e) { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 2501c2cc9..61dcbf397 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -7,7 +7,7 @@ namespace PubnubApi.EventEngine.Subscribe.States { - public class ReceiveReconnectingState : Core.State + public class ReceiveReconnectingState : SubscriptionState { public IEnumerable Channels; public IEnumerable ChannelGroups; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index c082f5242..6cd71698e 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -7,14 +7,11 @@ namespace PubnubApi.EventEngine.Subscribe.States { - public class ReceiveStoppedState : Core.State + public class ReceiveStoppedState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - public ReconnectionConfiguration ReconnectionConfiguration; - public override TransitionResult Transition(IEvent e) + public override TransitionResult Transition(IEvent e) { return e switch { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index dfb7c205b..010541e09 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -7,14 +7,11 @@ namespace PubnubApi.EventEngine.Subscribe.States { - public class ReceivingState : Core.State + public class ReceivingState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; - public ReconnectionConfiguration ReconnectionConfiguration; - public override IEnumerable OnEntry => new ReceiveMessagesInvocation() + public override IEnumerable OnEntry => new ReceiveMessagesInvocation() { Channels = this.Channels,ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); public override IEnumerable OnExit { get; } = new CancelReceiveMessagesInvocation().AsArray(); diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index cfa44f6ad..132a0a06d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -1,15 +1,14 @@ 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 : Core.State + public class UnsubscribedState : SubscriptionState { - public ReconnectionConfiguration ReconnectionConfiguration; - public override TransitionResult Transition(Core.IEvent e) { return e switch From 206713b8420ae8f1f6777dbfe647f727a0ea3edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Mon, 31 Jul 2023 14:09:23 +0200 Subject: [PATCH 66/71] wip: event engine delegates exposed --- .../EventEngine/Core/EffectDispatcher.cs | 4 ++++ src/Api/PubnubApi/EventEngine/Core/Engine.cs | 21 +++++++++++++++++++ .../PubnubApi/EventEngine/Core/EventQueue.cs | 8 +++++++ 3 files changed, 33 insertions(+) diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs index d0f3b6488..bad03b562 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -8,6 +8,8 @@ public class EffectDispatcher { private readonly Dictionary effectInvocationHandlerMap = new Dictionary(); + public event System.Action OnEffectDispatch; + /// /// Dispatch an invocation i.e. call a registered effect handler. /// @@ -15,6 +17,8 @@ public async Task Dispatch(T invocation) where T : IEffectInvocation { 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(); diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 7722a4d89..e2062127a 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -11,6 +11,25 @@ public abstract class Engine { 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; @@ -22,12 +41,14 @@ public Engine() { 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; diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index 8d3c9df88..e22991e0b 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -33,6 +33,14 @@ private IEvent Dequeue() } } + public IEvent Peek() + { + lock (lockObj) + { + return eventQueue.Peek(); + } + } + public async Task Loop(System.Func> function) { while (Count > 0) From 57f4fb2c085d7ad190377ca00bab0df3a08f93c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Wed, 2 Aug 2023 12:54:28 +0200 Subject: [PATCH 67/71] wip: preliminary potential fix for dispatcher --- .../EventEngine/Core/EffectDispatcher.cs | 4 ++-- src/Api/PubnubApi/EventEngine/Core/Utils.cs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs index bad03b562..1a326159e 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -13,7 +13,7 @@ public class EffectDispatcher { /// /// Dispatch an invocation i.e. call a registered effect handler. /// - public async Task Dispatch(T invocation) where T : IEffectInvocation { + public async Task Dispatch(IEffectInvocation invocation) { if (!effectInvocationHandlerMap.ContainsKey(invocation.GetType())) { throw new ArgumentException($"No handler for {invocation.GetType().Name} found."); } @@ -24,7 +24,7 @@ public async Task Dispatch(T invocation) where T : IEffectInvocation { await effectInvocationHandlerMap[invocation.GetType()].Cancel(); } else { - var handler = ((IEffectHandler)effectInvocationHandlerMap[invocation.GetType()]); + var handler = effectInvocationHandlerMap[invocation.GetType()]; if (handler.IsBackground(invocation)) handler.Run(invocation).Start(); else diff --git a/src/Api/PubnubApi/EventEngine/Core/Utils.cs b/src/Api/PubnubApi/EventEngine/Core/Utils.cs index 8657d6fa7..6436a262a 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Utils.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Utils.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using System.Collections.Generic; +using System.Reflection; namespace PubnubApi.EventEngine.Core { @@ -17,6 +18,20 @@ 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 From 3569933962a89fab0c17e7f97a2cc3a62281fb33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= <106665140+MikeDobrzan@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:23:36 +0200 Subject: [PATCH 68/71] Eventengine/effects abstraction (#184) * wip: effects abstraction overhaul * fix: added comments --- .../EventEngine/Core/EventEngineInterfaces.cs | 190 +++++++++++++----- .../Subscribe/Effects/EmitMessagesHandler.cs | 8 +- .../Effects/EmitStatusEffectHandler.cs | 8 +- .../Effects/HandshakeEffectHandler.cs | 13 +- .../Effects/ReceivingEffectHandler.cs | 13 +- 5 files changed, 158 insertions(+), 74 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 2d5aad22f..53c5ccee5 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -2,56 +2,142 @@ using System.Threading.Tasks; using System.Collections.Generic; -namespace PubnubApi.EventEngine.Core { - - /// - /// Generic effect handler. - /// - public interface IEffectHandler { - Task Cancel(); - } - - /// - /// 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); - } - - /// - /// 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); - } - } +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; + } + + + /// + /// 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/Subscribe/Effects/EmitMessagesHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs index 9ace826a1..af2d1f10f 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs @@ -7,7 +7,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - public class EmitMessagesHandler : IEffectHandler + public class EmitMessagesHandler : EffectHandler { private readonly System.Action> messageEmitterFunction; private readonly Pubnub pubnubInstance; @@ -19,7 +19,7 @@ public EmitMessagesHandler(Pubnub pubnubInstance, this.pubnubInstance = pubnubInstance; } - public async Task Run(EmitMessagesInvocation invocation) + public async override Task Run(EmitMessagesInvocation invocation) { var processedMessages = invocation.Messages.Messages.Select(m => new PNMessageResult() { @@ -37,9 +37,9 @@ public async Task Run(EmitMessagesInvocation invocation) } } - public bool IsBackground(EmitMessagesInvocation invocation) => false; + public override bool IsBackground(EmitMessagesInvocation invocation) => false; - public Task Cancel() + public override Task Cancel() { throw new NotImplementedException(); } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs index 366730ed5..1f207894d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitStatusEffectHandler.cs @@ -5,7 +5,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { - public class EmitStatusEffectHandler: IEffectHandler + public class EmitStatusEffectHandler: EffectHandler { private readonly Action statusEmitterFunction; private readonly Pubnub pubnubInstance; @@ -16,11 +16,11 @@ public EmitStatusEffectHandler(Pubnub pn, Action statusEmitter this.pubnubInstance = pn; } - public Task Cancel() => Utils.EmptyTask; + public override Task Cancel() => Utils.EmptyTask; - public bool IsBackground(EmitStatusInvocation invocation) => false; + public override bool IsBackground(EmitStatusInvocation invocation) => false; - public async Task Run(EmitStatusInvocation invocation) + public override async Task Run(EmitStatusInvocation invocation) { this.statusEmitterFunction(this.pubnubInstance, invocation.Status); } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index 1c214c5f3..d0fc32aaf 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -15,8 +15,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { public class HandshakeEffectHandler : - Core.IEffectHandler, - Core.IEffectHandler + EffectDoubleCancellableHandler { private SubscribeManager2 manager; private EventQueue eventQueue; @@ -29,7 +28,7 @@ internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue this.eventQueue = eventQueue; } - public async Task Run(HandshakeReconnectInvocation invocation) + public override async Task Run(HandshakeReconnectInvocation invocation) { if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) { @@ -44,12 +43,12 @@ public async Task Run(HandshakeReconnectInvocation invocation) } } - public bool IsBackground(HandshakeReconnectInvocation invocation) + public override bool IsBackground(HandshakeReconnectInvocation invocation) { return true; } - public async Task Run(HandshakeInvocation invocation) + public override async Task Run(HandshakeInvocation invocation) { var response = await MakeHandshakeRequest(invocation); @@ -71,7 +70,7 @@ public async Task Run(HandshakeInvocation invocation) } } - public bool IsBackground(HandshakeInvocation invocation) + public override bool IsBackground(HandshakeInvocation invocation) { return false; } @@ -105,7 +104,7 @@ public bool IsBackground(HandshakeInvocation invocation) } } - public async Task Cancel() + public override async Task Cancel() { if (!retryDelay.Cancelled) { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs index 98e2582d4..ab02868d3 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -15,8 +15,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { public class ReceivingEffectHandler: - Core.IEffectHandler, - Core.IEffectHandler + EffectDoubleCancellableHandler { private SubscribeManager2 manager; private EventQueue eventQueue; @@ -29,7 +28,7 @@ internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue this.eventQueue = eventQueue; } - public Task Run(ReceiveReconnectInvocation invocation) + public override Task Run(ReceiveReconnectInvocation invocation) { if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) { @@ -45,12 +44,12 @@ public Task Run(ReceiveReconnectInvocation invocation) return Utils.EmptyTask; } - public bool IsBackground(ReceiveReconnectInvocation invocation) + public override bool IsBackground(ReceiveReconnectInvocation invocation) { return true; } - public async Task Run(ReceiveMessagesInvocation invocation) + public override async Task Run(ReceiveMessagesInvocation invocation) { var response = await MakeReceiveMessagesRequest(invocation); var cursor = new SubscriptionCursor() @@ -76,7 +75,7 @@ public async Task Run(ReceiveMessagesInvocation invocation) } } - public bool IsBackground(ReceiveMessagesInvocation invocation) + public override bool IsBackground(ReceiveMessagesInvocation invocation) { return true; } @@ -105,7 +104,7 @@ public bool IsBackground(ReceiveMessagesInvocation invocation) } } - public async Task Cancel() + public override async Task Cancel() { if (!retryDelay.Cancelled) { From 276590caa85f5866c0a7f2193e881d5bc3635319 Mon Sep 17 00:00:00 2001 From: Pandu Masabathula Date: Fri, 4 Aug 2023 15:01:27 +0530 Subject: [PATCH 69/71] state transition unit tests (#182) * unit test cases for all subscribe state transtiions --- .../Subscribe/Events/SubscriptionEvents.cs | 3 + .../Invocations/SubscriptionInvocations.cs | 4 + .../Subscribe/States/HandshakeFailedState.cs | 3 +- .../States/HandshakeReconnectingState.cs | 2 +- .../Subscribe/States/HandshakeStoppedState.cs | 3 +- .../Subscribe/States/HandshakingState.cs | 3 +- .../Subscribe/States/ReceiveFailedState.cs | 6 +- .../Subscribe/States/ReceiveStoppedState.cs | 6 +- src/Api/PubnubApi/Model/Consumer/PNStatus.cs | 2 +- .../HandshakeFailedStateTransition.cs | 81 +++ .../HandshakeReconnectingStateTransition.cs | 176 +++++++ .../HandshakeStoppedStateTransition.cs | 81 +++ .../EventEngine/HandshakingStateTransition.cs | 169 ++++++ .../ReceiveFailedStateTransition.cs | 78 +++ .../ReceiveReconnectingStateTransition.cs | 178 +++++++ .../ReceiveStoppedStateTransition.cs | 95 ++++ .../EventEngine/ReceivingStateTransition.cs | 155 ++++++ .../UnsubscribedStateTransition.cs | 74 +++ .../PubnubApi.Tests/EventEngineTests.cs | 483 ------------------ .../PubnubApiPCL.Tests.csproj | 13 +- 20 files changed, 1120 insertions(+), 495 deletions(-) create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeStoppedStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs create mode 100644 src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs delete mode 100644 src/UnitTests/PubnubApi.Tests/EventEngineTests.cs diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index 605826a14..c8334f8de 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -27,11 +27,14 @@ public class HandshakeFailureEvent : Core.IEvent { } 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 ? diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs index fe474e164..c4e4773bc 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -21,6 +21,10 @@ public class EmitStatusInvocation : Core.IEffectInvocation { public EmitStatusInvocation(PNStatus status) { this.Status = status; + if (status != null) + { + this.StatusCategory = status.Category; + } } public EmitStatusInvocation(PNStatusCategory category) diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs index 9ef3286d1..f01083e28 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -29,10 +29,11 @@ public override TransitionResult Transition(IEvent e) { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index d84c4ccbc..eae980448 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -68,7 +68,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }.With(new EmitStatusInvocation(handshakeReconnectSuccess.Status)), - Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 55618c04d..db3455832 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -29,10 +29,11 @@ public override TransitionResult Transition(IEvent e) { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, + Cursor = reconnect.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 7272f73e7..457f9316a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -9,6 +9,7 @@ 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(); @@ -31,7 +32,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new States.HandshakingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index 3edcfd641..9fb34c77f 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -23,7 +23,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, @@ -31,7 +31,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.ReconnectEvent reconnect => new ReceivingState() + Events.ReconnectEvent reconnect => new HandshakingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, @@ -39,7 +39,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 6cd71698e..93c5236bd 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -20,7 +20,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() + Events.SubscriptionChangedEvent subscriptionChanged => new ReceiveStoppedState() { Channels = subscriptionChanged.Channels, ChannelGroups = subscriptionChanged.ChannelGroups, @@ -28,7 +28,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.ReconnectEvent reconnect => new ReceivingState() + Events.ReconnectEvent reconnect => new HandshakingState() { Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, @@ -36,7 +36,7 @@ public override TransitionResult Transition(IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new ReceiveStoppedState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, diff --git a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs index 4e3e3a5ff..aceb340e7 100644 --- a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs +++ b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs @@ -18,7 +18,7 @@ public PNStatus(Exception e, PNOperationType operationType, PNStatusCategory cat { this.Error = true; this.Operation = operationType; - this.ErrorData = new PNErrorData(e.Message, e); + this.ErrorData = new PNErrorData(e?.Message, e); this.AffectedChannels = affectedChannels?.ToList(); this.AffectedChannelGroups = affectedChannelGroups?.ToList(); this.Category = category; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs new file mode 100644 index 000000000..f65a31685 --- /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..85c4360cc --- /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..848403a57 --- /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..fd92a2209 --- /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/EventEngineTests.cs b/src/UnitTests/PubnubApi.Tests/EventEngineTests.cs deleted file mode 100644 index 52ff5f8d2..000000000 --- a/src/UnitTests/PubnubApi.Tests/EventEngineTests.cs +++ /dev/null @@ -1,483 +0,0 @@ -using System; -using System.Text; -using System.Collections.Generic; -using NUnit.Framework; -using PubnubApi; -using System.Text.RegularExpressions; -using System.Globalization; -using Newtonsoft.Json; -using System.Diagnostics; -using PubnubApi.PubnubEventEngine; - -namespace PubNubMessaging.Tests -{ - [TestFixture] - public class EventEngineTests - { - EventEngine pnEventEngine { get; set; } - State unsubscribeState; - State handshakingState; - State handshakeReconnectingState; - State handshakeStoppedState; - State handshakeFailedState; - State receivingState; - State receiveReconnectingState; - - [SetUp] - public void Init() - { - IPubnubUnitTest pubnubUnitTest = new PubnubUnitTest(); - var effectDispatcher = new EffectDispatcher(); - var eventEmitter = new EventEmitter(); - pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); - pnEventEngine.PubnubUnitTest = pubnubUnitTest; - pnEventEngine.Setup(new PNConfiguration(new UserId("testuserid"))); - } - - [Test] - public void TestWhenStateTypeUnsubscribed() - { - //Unsubscribed => SubscriptionChanged => Handshaking - pnEventEngine.CurrentState = new State(StateType.Unsubscribed) { EventType = EventType.SubscriptionChanged }; - State currentHandshakingState = pnEventEngine.NextState(); - if (currentHandshakingState.StateType == StateType.Handshaking) - { - //Expected result. - } - else - { - Assert.Fail("Unsubscribed + SubscriptionChanged => Handshaking FAILED"); - return; - } - - //Unsubscribed => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.Unsubscribed) { EventType = EventType.SubscriptionRestored }; - State currentReceiveState = pnEventEngine.NextState(); - if (currentReceiveState.StateType == StateType.Receiving) - { - //Expected result. - } - else - { - Assert.Fail("Unsubscribed + SubscriptionRestored => Receiving FAILED"); - return; - } - - } - - [Test] - public void TestWhenStateTypeHandshaking() - { - //Handshaking => SubscriptionChanged => Handshaking - pnEventEngine.CurrentState = new State(StateType.Handshaking) { EventType = EventType.SubscriptionChanged }; - State currentHandshakingState = pnEventEngine.NextState(); - if (currentHandshakingState.StateType == StateType.Handshaking) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("Handshaking + SubscriptionChanged => Handshaking"); - return; - } - - //Handshaking => HandshakeFailure => HandshakeReconnecting - pnEventEngine.CurrentState = new State(StateType.Handshaking) { EventType = EventType.HandshakeFailure }; - State currentHandshakeReconnectingState = pnEventEngine.NextState(); - if (currentHandshakeReconnectingState.StateType == StateType.HandshakeReconnecting) - { - //empty - } - else - { - Assert.Fail("Handshaking + HandshakeFailure => HandshakeReconnecting FAILED"); - return; - } - - //Handshaking => Disconnect => HandshakeStopped - pnEventEngine.CurrentState = new State(StateType.Handshaking) { EventType = EventType.Disconnect }; - State currentHandshakeStoppedState = pnEventEngine.NextState(); - if (currentHandshakeStoppedState.StateType == StateType.HandshakeStopped) - { - //empty - } - else - { - Assert.Fail("Handshaking + Disconnect => HandshakeStopped FAILED"); - return; - } - - //Handshaking => HandshakeSuccess => Receiving - pnEventEngine.CurrentState = new State(StateType.Handshaking) { EventType = EventType.HandshakeSuccess }; - State currentReceivingState = pnEventEngine.NextState(); - if (currentReceivingState.StateType == StateType.Receiving) - { - //empty - } - else - { - Assert.Fail("Handshaking + HandshakeSuccess => Receiving FAILED"); - return; - } - - //Handshaking => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.Handshaking) { EventType = EventType.SubscriptionRestored }; - currentReceivingState = pnEventEngine.NextState(); - if (currentReceivingState.StateType == StateType.Receiving) - { - //empty - } - else - { - Assert.Fail("Handshaking + SubscriptionRestored => Receiving FAILED"); - return; - } - } - - [Test] - public void TestWhenStateTypeHandshakeReconnecting() - { - //HandshakeReconnecting => SubscriptionChanged => Handshaking - pnEventEngine.CurrentState = new State(StateType.HandshakeReconnecting) { EventType = EventType.SubscriptionChanged }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Handshaking) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeReconnecting + SubscriptionChanged => Handshaking"); - return; - } - - //HandshakeReconnecting => HandshakeReconnectFailure => HandshakeReconnecting - pnEventEngine.CurrentState = new State(StateType.HandshakeReconnecting) { EventType = EventType.HandshakeReconnectFailure }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.HandshakeReconnecting) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeReconnecting + HandshakeReconnectFailure => HandshakeReconnecting"); - return; - } - - //HandshakeReconnecting => Disconnect => HandshakeStopped - pnEventEngine.CurrentState = new State(StateType.HandshakeReconnecting) { EventType = EventType.Disconnect }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.HandshakeStopped) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeReconnecting + Disconnect => HandshakeStopped"); - return; - } - - //HandshakeReconnecting => HandshakeReconnectGiveUp => HandshakeFailed - pnEventEngine.CurrentState = new State(StateType.HandshakeReconnecting) { EventType = EventType.HandshakeReconnectGiveUp }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.HandshakeFailed) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeReconnecting + HandshakeReconnectGiveUp => HandshakeFailed"); - return; - } - - // - //HandshakeReconnecting => HandshakeReconnectSuccess => Receiving - pnEventEngine.CurrentState = new State(StateType.HandshakeReconnecting) { EventType = EventType.HandshakeReconnectSuccess }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeReconnecting + HandshakeReconnectSuccess => Receiving"); - return; - } - - //HandshakeReconnecting => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.HandshakeReconnecting) { EventType = EventType.SubscriptionRestored }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeReconnecting + SubscriptionRestored => Receiving"); - return; - } - - } - - [Test] - public void TestWhenStateTypeHandshakeFailed() - { - //HandshakeFailed => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.HandshakeFailed) { EventType = EventType.SubscriptionRestored }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeFailed + SubscriptionRestored => ReceiveReconnecting"); - return; - } - - //HandshakeFailed => SubscriptionChanged => Handshaking - pnEventEngine.CurrentState = new State(StateType.HandshakeFailed) { EventType = EventType.SubscriptionChanged }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Handshaking) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeFailed + SubscriptionChanged => Handshaking"); - return; - } - - - //HandshakeFailed => Reconnect => Handshaking - pnEventEngine.CurrentState = new State(StateType.HandshakeFailed) { EventType = EventType.Reconnect }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Handshaking) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeFailed + Reconnect => Handshaking"); - return; - } - } - - [Test] - public void TestWhenStateTypeHandshakeStopped() - { - //HandshakeStopped => Reconnect => Handshaking - pnEventEngine.CurrentState = new State(StateType.HandshakeStopped) { EventType = EventType.Reconnect }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Handshaking) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("HandshakeStopped + Reconnect => Handshaking"); - return; - } - } - - - [Test] - public void TestWhenStateTypeReceiving() - { - //Receiving => SubscriptionChanged => Receiving - pnEventEngine.CurrentState = new State(StateType.Receiving) { EventType = EventType.SubscriptionChanged }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("Receiving + SubscriptionChanged => Receiving"); - return; - } - - //Receiving => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.Receiving) { EventType = EventType.SubscriptionRestored }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("Receiving + SubscriptionRestored => Receiving"); - return; - } - - //Receiving => ReceiveSuccess => Receiving - pnEventEngine.CurrentState = new State(StateType.Receiving) { EventType = EventType.ReceiveSuccess }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("Receiving + ReceiveSuccess => Receiving"); - return; - } - - //Receiving => ReceiveFailure => ReceiveReconnecting - pnEventEngine.CurrentState = new State(StateType.Receiving) { EventType = EventType.ReceiveFailure }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.ReceiveReconnecting) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("Receiving + ReceiveFailure => ReceiveReconnecting"); - return; - } - - //Receiving => Disconnect => ReceiveStopped - pnEventEngine.CurrentState = new State(StateType.Receiving) { EventType = EventType.Disconnect }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.ReceiveStopped) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("Receiving + Disconnect => ReceiveStopped"); - return; - } - } - - [Test] - public void TestWhenStateTypeReceiveReconnecting() - { - //ReceiveReconnecting => SubscriptionChanged => Receiving - pnEventEngine.CurrentState = new State(StateType.ReceiveReconnecting) { EventType = EventType.SubscriptionChanged }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveReconnecting + SubscriptionChanged => Receiving"); - return; - } - - //ReceiveReconnecting => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.ReceiveReconnecting) { EventType = EventType.SubscriptionRestored }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveReconnecting + SubscriptionRestored => Receiving"); - return; - } - - //ReceiveReconnecting => ReceiveReconnectFailure => ReceiveReconnecting - pnEventEngine.CurrentState = new State(StateType.ReceiveReconnecting) { EventType = EventType.ReceiveReconnectFailure }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.ReceiveReconnecting) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveReconnecting + ReceiveReconnectFailure => ReceiveReconnecting"); - return; - } - - //ReceiveReconnecting => ReceiveReconnectGiveUp => ReceiveFailed - pnEventEngine.CurrentState = new State(StateType.ReceiveReconnecting) { EventType = EventType.ReceiveReconnectGiveUp }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.ReceiveFailed) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveReconnecting + ReceiveReconnectGiveUp => ReceiveFailed"); - return; - } - - //ReceiveReconnecting => Disconnect => ReceiveStopped - pnEventEngine.CurrentState = new State(StateType.ReceiveReconnecting) { EventType = EventType.Disconnect }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.ReceiveStopped) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveReconnecting + Disconnect => ReceiveStopped"); - return; - } - - } - - [Test] - public void TestWhenStateTypeReceiveFailed() - { - //ReceiveFailed => SubscriptionChanged => Receiving - pnEventEngine.CurrentState = new State(StateType.ReceiveFailed) { EventType = EventType.SubscriptionChanged }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveFailed + SubscriptionChanged => Receiving"); - return; - } - - //ReceiveFailed => SubscriptionRestored => Receiving - pnEventEngine.CurrentState = new State(StateType.ReceiveFailed) { EventType = EventType.SubscriptionRestored }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveFailed + SubscriptionRestored => Receiving"); - return; - } - - //ReceiveFailed => Reconnect => Receiving - pnEventEngine.CurrentState = new State(StateType.ReceiveFailed) { EventType = EventType.Reconnect }; - currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveFailed + Reconnect => Receiving"); - return; - } - } - - [Test] - public void TestWhenStateTypeReceiveStopped() - { - //ReceiveStopped => Reconnect => Receiving - pnEventEngine.CurrentState = new State(StateType.ReceiveStopped) { EventType = EventType.Reconnect }; - State currentNewState = pnEventEngine.NextState(); - if (currentNewState.StateType == StateType.Receiving) - { - //Continue for further tests on transition - } - else - { - Assert.Fail("ReceiveStopped + Reconnect => Receiving"); - return; - } - - } - } -} diff --git a/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj b/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj index 4adfe7b1a..714a4e134 100644 --- a/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj +++ b/src/UnitTests/PubnubApiPCL.Tests/PubnubApiPCL.Tests.csproj @@ -51,7 +51,15 @@ - + + + + + + + + + @@ -111,6 +119,9 @@ Resource.Designer.cs + + + From 34097c51fe9ece21243d5f76c4b2ac3b9c2ebfe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobrza=C5=84ski?= Date: Mon, 7 Aug 2023 08:55:10 +0200 Subject: [PATCH 70/71] added cancel reconnect invocations support --- .../EventEngine/Core/EventEngineInterfaces.cs | 22 +++++++++++++++++++ .../Effects/HandshakeEffectHandler.cs | 2 +- .../Effects/ReceivingEffectHandler.cs | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index 53c5ccee5..c58119d7a 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -98,6 +98,28 @@ public Task Run(T3 invocation) 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; + } /// diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index d0fc32aaf..8702d7480 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -15,7 +15,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { public class HandshakeEffectHandler : - EffectDoubleCancellableHandler + EffectDoubleCancellableHandler { private SubscribeManager2 manager; private EventQueue eventQueue; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs index ab02868d3..6810ae4a2 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -15,7 +15,7 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { public class ReceivingEffectHandler: - EffectDoubleCancellableHandler + EffectDoubleCancellableHandler { private SubscribeManager2 manager; private EventQueue eventQueue; From f7f6d8bbdc84fca2bec61e0f18f8f3ebf8404b47 Mon Sep 17 00:00:00 2001 From: Mohit Tejani <60129002+mohitpubnub@users.noreply.github.com> Date: Mon, 7 Aug 2023 17:09:04 +0530 Subject: [PATCH 71/71] subscribe event engine integration (#185) * StateTransitions - Tests - WIP * refactor handshake unit tests * HandshakeFailedState tests * HandshakeStopped tests * ReceivingState tests * ReceiveReconnectingState tests * ReceiveReconnectingState - more tests * ReceiveFaileState and ReceiveStoppedState * Any UnsubscribeEvent tests * Separated tests to individual states * HandshakingState emit status * invocations and refactor * Refactored HandshakeFailedStateTransition * take-1: integration * HandshakeReconnecting HandshakeStopped state transitions refactor * HandshakingStateTransition refactor * listeners * ReceiveFailedStateTransition refactor * refactored remaining transitions * fix: emitters * moved subscribe event engine initialisation to subscribeOpertaion * manager response object - WIP * ee integration changes * merge remianing changes * expose unsubscribeAll * manager wip --------- Co-authored-by: Pandu Masabathula --- .../EndPoint/PubSub/SubscribeManager2.cs | 247 +++--------------- .../EndPoint/PubSub/SubscribeOperation2.cs | 192 ++++---------- .../EventEngine/Core/EffectDispatcher.cs | 2 +- src/Api/PubnubApi/EventEngine/Core/Engine.cs | 8 +- .../Context/ReconnectionConfiguration.cs | 4 +- .../Context/ReconnectionDelayUtil.cs | 1 + .../Subscribe/Effects/EmitMessagesHandler.cs | 8 +- .../Effects/HandshakeEffectHandler.cs | 32 +-- .../Effects/ReceivingEffectHandler.cs | 17 +- .../Subscribe/SubscribeEventEngine.cs | 38 ++- .../Subscribe/SubscribeEventEngineFactory.cs | 43 +++ src/Api/PubnubApi/Model/Consumer/PNStatus.cs | 6 +- src/Api/PubnubApi/Pubnub.cs | 18 +- src/Api/PubnubApiPCL/PubnubApiPCL.csproj | 3 + src/Api/PubnubApiUWP/PubnubApiUWP.csproj | 3 + .../HandshakeFailedStateTransition.cs | 2 +- .../HandshakeReconnectingStateTransition.cs | 2 +- .../EventEngine/ReceivingStateTransition.cs | 2 +- .../UnsubscribedStateTransition.cs | 2 +- 19 files changed, 207 insertions(+), 423 deletions(-) create mode 100644 src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs index 56e2e6d96..a84db0422 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs @@ -7,6 +7,7 @@ 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; @@ -92,38 +93,30 @@ public SubscribeManager2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jso #pragma warning disable - internal async Task> HandshakeRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + public async Task> HandshakeRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) { - Tuple resp = new Tuple ("", null); + string channelsJsonState = BuildJsonUserState(channels, channelGroups, false); - 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); - 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; - 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 - - await UrlProcessRequest(request, pubnubRequestState, false).ContinueWith(r => - { - resp = r.Result; - }, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false); - } - catch(Exception ex) + Tuple responseTuple = await UrlProcessRequest(request, pubnubRequestState, false).ConfigureAwait(false); + if (!string.IsNullOrEmpty(responseTuple.Item1) && responseTuple.Item2 == null) { - 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; + 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() @@ -150,9 +143,9 @@ internal void HandshakeRequestCancellation() 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> ReceiveRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + internal async Task, PNStatus>> ReceiveRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) { - Tuple resp = new Tuple ("", null); + Tuple, PNStatus> resp = new Tuple, PNStatus>(null, null); try { @@ -161,7 +154,7 @@ internal async Task> ReceiveRequest(PNOperationType r 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(); + RequestState> pubnubRequestState = new RequestState>(); pubnubRequestState.Channels = channels; pubnubRequestState.ChannelGroups = channelGroups; pubnubRequestState.ResponseType = responseType; @@ -171,11 +164,14 @@ internal async Task> ReceiveRequest(PNOperationType r pubnubRequestState.TimeQueued = DateTime.Now; // Wait for message - - await UrlProcessRequest(request, pubnubRequestState, false).ContinueWith(r => - { - resp = r.Result; - }, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false); + 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) { @@ -254,29 +250,10 @@ internal protected async Task> UrlProcessRequest(Uri try { - if (terminateCurrentSubRequest) - { - //TerminateCurrentSubscriberRequest(); - } - - //if (PubnubInstance == null) - //{ - // System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, PubnubInstance is null. Exiting UrlProcessRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture))); - // return new Tuple("", null); - //} - if (pubnubRequestState != null) { channel = (pubnubRequestState.Channels != null && pubnubRequestState.Channels.Length > 0) ? string.Join(",", pubnubRequestState.Channels.OrderBy(x => x).ToArray()) : ","; - //if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && !channel.Equals(",", StringComparison.OrdinalIgnoreCase) && !ChannelRequest[PubnubInstance.InstanceId].ContainsKey(channel) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) - //{ - // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - // { - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, UrlProcessRequest ChannelRequest PubnubInstance.InstanceId Channel NOT matching", DateTime.Now.ToString(CultureInfo.InvariantCulture)), currentConfig.LogVerbosity); - // } - // return new Tuple("", null); - //} } #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 @@ -298,23 +275,8 @@ internal protected async Task> UrlProcessRequest(Uri pubnubRequestState.Request = request; httpSubscribe = request; - - //if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) - //{ - // ChannelRequest[PubnubInstance.InstanceId].AddOrUpdate(channel, pubnubRequestState.Request, (key, oldState) => pubnubRequestState.Request); - //} #endif - //if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - //{ - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Request={1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), requestUri.ToString()), currentConfig.LogVerbosity); - //} - - //if (pubnubRequestState != null && pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation) - //{ - // SubscribeRequestTracker.AddOrUpdate(PubnubInstance.InstanceId, DateTime.Now, (key, oldState) => DateTime.Now); - //} - string jsonString = ""; #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 if (pubnubRequestState != null && pubnubRequestState.UsePostMethod) @@ -344,30 +306,8 @@ internal protected async Task> UrlProcessRequest(Uri } #endif - //if (SubscribeDisconnected.ContainsKey(PubnubInstance.InstanceId) && SubscribeDisconnected[PubnubInstance.InstanceId]) - //{ - // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - // { - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0},Received JSON but SubscribeDisconnected = {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonString, requestUri), currentConfig.LogVerbosity); - // } - // throw new OperationCanceledException("Disconnected"); - //} - - //if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - //{ - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, JSON= {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonString, requestUri), currentConfig.LogVerbosity); - //} PNStatus errStatus = GetStatusIfError(pubnubRequestState, jsonString); - if (errStatus == null && pubnubRequestState != null) - { - PNStatus status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(pubnubRequestState.ResponseType, PNStatusCategory.PNAcknowledgmentCategory, pubnubRequestState, (int)HttpStatusCode.OK, null); - return new Tuple(jsonString, status); - } - else - { - jsonString = ""; - return new Tuple(jsonString, errStatus); - } + return new Tuple((errStatus == null) ? jsonString : "", errStatus); } catch (Exception ex) { @@ -433,29 +373,6 @@ protected string BuildJsonUserState(string channel, string channelGroup, bool lo throw new ArgumentException("BuildJsonUserState takes either channel or channelGroup at one time. Send one at a time by passing empty value for other."); } - //if (local) - //{ - // if (!string.IsNullOrEmpty(channel) && ChannelLocalUserState[PubnubInstance.InstanceId].ContainsKey(channel)) - // { - // ChannelLocalUserState[PubnubInstance.InstanceId].TryGetValue(channel, out channelUserStateDictionary); - // } - // if (!string.IsNullOrEmpty(channelGroup) && ChannelGroupLocalUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) - // { - // ChannelGroupLocalUserState[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupUserStateDictionary); - // } - //} - //else - //{ - // if (!string.IsNullOrEmpty(channel) && ChannelUserState.ContainsKey(PubnubInstance.InstanceId) && ChannelUserState[PubnubInstance.InstanceId].ContainsKey(channel)) - // { - // ChannelUserState[PubnubInstance.InstanceId].TryGetValue(channel, out channelUserStateDictionary); - // } - // if (!string.IsNullOrEmpty(channelGroup)&& ChannelGroupUserState.ContainsKey(PubnubInstance.InstanceId) && ChannelGroupUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) - // { - // ChannelGroupUserState[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupUserStateDictionary); - // } - //} - StringBuilder jsonStateBuilder = new StringBuilder(); if (channelUserStateDictionary != null) @@ -690,109 +607,9 @@ internal List WrapResultBasedOnResponseType(PNOperationType type, str internal bool Disconnect() { - //if (SubscribeDisconnected[PubnubInstance.InstanceId]) - //{ - // return false; - //} - //LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager Manual Disconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); - //SubscribeDisconnected[PubnubInstance.InstanceId] = true; - //TerminateCurrentSubscriberRequest(); - //PubnubCoreBase2.TerminatePresenceHeartbeatTimer(); - //TerminateReconnectTimer(); - return true; } - private void RegisterPresenceHeartbeatTimer(string[] channels, string[] channelGroups) - { - //if (PresenceHeartbeatTimer != null) - //{ - // try - // { - // PresenceHeartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite); - // PresenceHeartbeatTimer.Dispose(); - // PresenceHeartbeatTimer = null; - // } - // catch { /* ignore */ } - //} - //if ((channels != null && channels.Length > 0 && channels.Where(s => s != null && s.Contains("-pnpres") == false).Any()) - // || (channelGroups != null && channelGroups.Length > 0 && channelGroups.Where(s => s != null && s.Contains("-pnpres") == false).Any())) - //{ - // RequestState presenceHeartbeatState = new RequestState(); - // presenceHeartbeatState.Channels = channels; - // presenceHeartbeatState.ChannelGroups = channelGroups; - // presenceHeartbeatState.ResponseType = PNOperationType.PNHeartbeatOperation; - // presenceHeartbeatState.Request = null; - // presenceHeartbeatState.Response = null; - - // if (config.ContainsKey(PubnubInstance.InstanceId) && config[PubnubInstance.InstanceId].PresenceInterval > 0) - // { - // PresenceHeartbeatTimer = new Timer(OnPresenceHeartbeatIntervalTimeout, presenceHeartbeatState, config[PubnubInstance.InstanceId].PresenceInterval * 1000, config[PubnubInstance.InstanceId].PresenceInterval * 1000); - // } - //} - } - -#pragma warning disable - void OnPresenceHeartbeatIntervalTimeout(System.Object presenceHeartbeatState) -#pragma warning restore - { - ////Make presence heartbeat call - //RequestState currentState = presenceHeartbeatState as RequestState; - //if (currentState != null) - //{ - // string[] subscriberChannels = (currentState.Channels != null) ? currentState.Channels.Where(s => s.Contains("-pnpres") == false).ToArray() : null; - // string[] subscriberChannelGroups = (currentState.ChannelGroups != null) ? currentState.ChannelGroups.Where(s => s.Contains("-pnpres") == false).ToArray() : null; - - // bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, currentState.ResponseType, currentState.PubnubCallback, currentState.Channels, currentState.ChannelGroups); - // if (networkConnection) - // { - // if ((subscriberChannels != null && subscriberChannels.Length > 0) || (subscriberChannelGroups != null && subscriberChannelGroups.Length > 0)) - // { - // string channelsJsonState = BuildJsonUserState(subscriberChannels, subscriberChannelGroups, false); - // IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); - - // Uri request = urlBuilder.BuildPresenceHeartbeatRequest("GET", "", subscriberChannels, subscriberChannelGroups, channelsJsonState); - - // RequestState requestState = new RequestState(); - // requestState.Channels = currentState.Channels; - // requestState.ChannelGroups = currentState.ChannelGroups; - // requestState.ResponseType = PNOperationType.PNHeartbeatOperation; - // requestState.PubnubCallback = null; - // requestState.Reconnect = false; - // requestState.Response = null; - // requestState.TimeQueued = DateTime.Now; - - // UrlProcessRequest(request, requestState, false).ContinueWith(r => - // { - // string json = r.Result.Item1; - // if (!string.IsNullOrEmpty(json)) - // { - // List result = ProcessJsonResponse(requestState, json); - // //ProcessResponseCallbacks(result, requestState); - // } - // }, TaskContinuationOptions.ExecuteSynchronously).Wait(); - // } - // } - // else - // { - // if (PubnubInstance != null && !networkConnection) - // { - // PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(PNOperationType.PNSubscribeOperation, PNStatusCategory.PNNetworkIssuesCategory, null, (int)System.Net.HttpStatusCode.NotFound, new PNException("Internet connection problem during presence heartbeat.")); - // if (subscriberChannels != null && subscriberChannels.Length > 0) - // { - // status.AffectedChannels.AddRange(subscriberChannels.ToList()); - // } - // if (subscriberChannelGroups != null && subscriberChannelGroups.Length > 0) - // { - // status.AffectedChannelGroups.AddRange(subscriberChannelGroups.ToList()); - // } - // Announce(status); - // } - - // } - //} - - } #region IDisposable Support private bool disposedValue; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs index 61b9d5c26..2a4f7282f 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs @@ -7,7 +7,8 @@ using System.Net; using System.Globalization; using PubnubApi.PubnubEventEngine; - +using PubnubApi.EventEngine.Subscribe; +using PubnubApi; namespace PubnubApi.EndPoint { @@ -26,15 +27,17 @@ public class SubscribeOperation2: ISubscribeOperation private bool presenceSubscribeEnabled; private SubscribeManager2 manager; private Dictionary queryParam; - private PubnubEventEngine.EventEngine pnEventEngine; private Pubnub PubnubInstance; - public List SubscribeListenerList + 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, Pubnub instance) + 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; @@ -43,67 +46,12 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j pubnubLog = log; pubnubTelemetryMgr = telemetryManager; pubnubTokenMgr = tokenManager; + this.subscribeEventEngineFactory = subscribeEventEngineFactory; + this.instanceId = instanceId; var eventEmitter = new EventEmitter(); eventEmitter.RegisterJsonListener(JsonCallback); - var handshakeEffectHandler = new HandshakeEffectHandler(eventEmitter); - handshakeEffectHandler.LogCallback = LogCallback; - handshakeEffectHandler.HandshakeRequested += HandshakeEffect_HandshakeRequested; - handshakeEffectHandler.CancelHandshakeRequested += HandshakeEffect_CancelHandshakeRequested; - handshakeEffectHandler.AnnounceStatus = Announce; - - var handshakeReconnectEffectHandler = new HandshakeReconnectEffectHandler(eventEmitter); - handshakeReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; - handshakeReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; - handshakeReconnectEffectHandler.LogCallback = LogCallback; - handshakeReconnectEffectHandler.HandshakeReconnectRequested += HandshakeReconnectEffect_HandshakeRequested; - handshakeReconnectEffectHandler.CancelHandshakeReconnectRequested += HandshakeReconnectEffect_CancelHandshakeRequested; - handshakeReconnectEffectHandler.AnnounceStatus = Announce; - - var handshakeFailedEffectHandler = new HandshakeFailedEffectHandler(eventEmitter); - handshakeFailedEffectHandler.LogCallback = LogCallback; - - var receivingEffectHandler = new ReceivingEffectHandler(eventEmitter); - receivingEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; - receivingEffectHandler.LogCallback = LogCallback; - receivingEffectHandler.ReceiveRequested += ReceivingEffect_ReceiveRequested; - receivingEffectHandler.CancelReceiveRequested += ReceivingEffect_CancelReceiveRequested; - receivingEffectHandler.AnnounceStatus = Announce; - receivingEffectHandler.AnnounceMessage = Announce; - receivingEffectHandler.AnnouncePresenceEvent = Announce; - - var receiveReconnectEffectHandler = new ReceiveReconnectingEffectHandler(eventEmitter); - receiveReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; - receiveReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; - receiveReconnectEffectHandler.LogCallback = LogCallback; - receiveReconnectEffectHandler.ReceiveReconnectRequested += ReceiveReconnectEffect_ReceiveRequested; - receiveReconnectEffectHandler.CancelReceiveReconnectRequested += ReceiveReconnectEffect_CancelReceiveRequested; - receiveReconnectEffectHandler.AnnounceStatus = Announce; - - var effectDispatcher = new EffectDispatcher(); - effectDispatcher.PubnubUnitTest = unit; - effectDispatcher.Register(EventType.Handshake,handshakeEffectHandler); - effectDispatcher.Register(EventType.CancelHandshake,handshakeEffectHandler); - effectDispatcher.Register(EventType.HandshakeSuccess, handshakeEffectHandler); - - effectDispatcher.Register(EventType.HandshakeFailure, handshakeFailedEffectHandler); - effectDispatcher.Register(EventType.CancelHandshakeFailure, handshakeFailedEffectHandler); - - effectDispatcher.Register(EventType.HandshakeReconnect, handshakeReconnectEffectHandler); - effectDispatcher.Register(EventType.CancelHandshakeReconnect, handshakeReconnectEffectHandler); - effectDispatcher.Register(EventType.HandshakeReconnectSuccess, handshakeReconnectEffectHandler); - effectDispatcher.Register(EventType.HandshakeReconnectGiveUp, handshakeReconnectEffectHandler); - - effectDispatcher.Register(EventType.ReceiveMessages, receivingEffectHandler); - effectDispatcher.Register(EventType.CancelReceiveMessages, receivingEffectHandler); - effectDispatcher.Register(EventType.ReceiveSuccess, receivingEffectHandler); - - effectDispatcher.Register(EventType.ReceiveReconnect, receiveReconnectEffectHandler); - effectDispatcher.Register(EventType.CancelReceiveReconnect, receiveReconnectEffectHandler); - effectDispatcher.Register(EventType.ReceiveReconnectSuccess, receiveReconnectEffectHandler); - effectDispatcher.Register(EventType.ReceiveReconnectGiveUp, receiveReconnectEffectHandler); - // pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); // pnEventEngine.PubnubUnitTest = unit; // pnEventEngine.Setup(config); @@ -118,29 +66,6 @@ public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary j // } } - private void ReceivingEffect_ReceiveRequested(object sender, ReceiveRequestEventArgs e) - { - Tuple resp = manager.ReceiveRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.ReceiveResponseCallback?.Invoke(jsonResp); - } - - private void HandshakeEffect_HandshakeRequested(object sender, HandshakeRequestEventArgs e) - { - Tuple resp = manager.HandshakeRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), 0, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.HandshakeResponseCallback?.Invoke(jsonResp); - } - - private void HandshakeReconnectEffect_HandshakeRequested(object sender, HandshakeReconnectRequestEventArgs e) - { - Tuple resp = manager.HandshakeRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), 0, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.HandshakeReconnectResponseCallback?.Invoke(jsonResp); - } private void HandshakeEffect_CancelHandshakeRequested(object sender, CancelHandshakeRequestEventArgs e) { manager.HandshakeRequestCancellation(); @@ -153,13 +78,6 @@ private void ReceivingEffect_CancelReceiveRequested(object sender, CancelReceive { manager.ReceiveRequestCancellation(); } - private void ReceiveReconnectEffect_ReceiveRequested(object sender, ReceiveReconnectRequestEventArgs e) - { - Tuple resp = manager.ReceiveRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.ReceiveReconnectResponseCallback?.Invoke(jsonResp); - } private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, CancelReceiveReconnectRequestEventArgs e) { manager.ReceiveReconnectRequestCancellation(); @@ -167,14 +85,6 @@ private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, Cancel private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) { - if (!string.IsNullOrEmpty(json)) - { - List respObject = manager.WrapResultBasedOnResponseType(PNOperationType.PNSubscribeOperation, json, pnEventEngine.Context.Channels.ToArray(), pnEventEngine.Context.ChannelGroups.ToArray()); - if (respObject != null && respObject.Count > 0) - { - ProcessListenerCallback(respObject, zeroTimeTokenRequest, messageCount, pnEventEngine.Context.Channels.ToArray(), pnEventEngine.Context.ChannelGroups.ToArray()); - } - } } protected void ProcessListenerCallback(List result, bool zeroTimeTokenRequest, int messageCount, string[] channels, string[] channelGroups) @@ -772,56 +682,29 @@ public void Execute() private void Subscribe(string[] channels, string[] channelGroups, Dictionary externalQueryParam) { - if ((channels == null || channels.Length == 0) && (channelGroups == null || channelGroups.Length == 0)) + 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."); - } - - string channel = (channels != null) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ""; - string channelGroup = (channelGroups != null) ? string.Join(",", channelGroups.OrderBy(x => x).ToArray()) : ""; - - PNPlatform.Print(config, pubnubLog); - - LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, requested subscribe for channel(s)={1} and channel group(s)={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel, channelGroup), config.LogVerbosity); + throw new ArgumentException("Either Channel Or Channel Group or Both should be provided."); + } - Dictionary initialSubscribeUrlParams = new Dictionary(); - if (this.subscribeTimetoken >= 0) + if (this.subscribeEventEngineFactory.hasEventEngine(instanceId)) { - initialSubscribeUrlParams.Add("tt", this.subscribeTimetoken.ToString(CultureInfo.InvariantCulture)); - } - if (!string.IsNullOrEmpty(config.FilterExpression) && config.FilterExpression.Trim().Length > 0) + subscribeEventEngine = subscribeEventEngineFactory.getEventEngine(instanceId); + } + else { - initialSubscribeUrlParams.Add("filter-expr", UriUtil.EncodeUriComponent(config.FilterExpression, PNOperationType.PNSubscribeOperation, false, false, false)); - } + 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); - -#if NETFX_CORE || WINDOWS_UWP || UAP || NETSTANDARD10 || NETSTANDARD11 || NETSTANDARD12 - Task.Factory.StartNew(() => - { - manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); - //manager.CurrentPubnubInstance(PubnubInstance); - pnEventEngine.Subscribe(channels.ToList(), channelGroups.ToList()); - //manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); - //manager.CurrentPubnubInstance(PubnubInstance); - //manager.MultiChannelSubscribeInit(PNOperationType.PNSubscribeOperation, channels, channelGroups, initialSubscribeUrlParams, externalQueryParam); - }, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false); -#else - new Thread(() => - { - manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); - //manager.CurrentPubnubInstance(PubnubInstance); - if (pnEventEngine.CurrentState == null) - { - pnEventEngine.InitialState(new State(StateType.Unsubscribed) { EventType = EventType.SubscriptionChanged}); - } - pnEventEngine.Subscribe(channels.ToList(), channelGroups.ToList()); - //manager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); - //manager.CurrentPubnubInstance(PubnubInstance); - //manager.MultiChannelSubscribeInit(PNOperationType.PNSubscribeOperation, channels, channelGroups, initialSubscribeUrlParams, externalQueryParam); - }) - { IsBackground = true }.Start(); -#endif - } + } + subscribeEventEngine.Subscribe(channels, channelGroups); + } internal bool Retry(bool reconnect) { @@ -863,5 +746,24 @@ 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/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs index 1a326159e..e4fea80fc 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -26,7 +26,7 @@ public async Task Dispatch(IEffectInvocation invocation) { { var handler = effectInvocationHandlerMap[invocation.GetType()]; if (handler.IsBackground(invocation)) - handler.Run(invocation).Start(); + await handler.Run(invocation); else await handler.Run(invocation); } diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index e2062127a..48ca50f08 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -3,7 +3,7 @@ namespace PubnubApi.EventEngine.Core { public abstract class Engine { - public EventQueue eventQueue = new EventQueue(); + public EventQueue EventQueue = new EventQueue(); protected EffectDispatcher dispatcher = new EffectDispatcher(); protected State currentState; @@ -32,18 +32,18 @@ public event System.Action OnEffectDispatch public event System.Action OnEventQueued; public Engine() { - eventQueue.OnEventQueued += OnEvent; + EventQueue.OnEventQueued += OnEvent; } ~Engine() { - eventQueue.OnEventQueued -= OnEvent; + EventQueue.OnEventQueued -= OnEvent; } private async void OnEvent(EventQueue q) { OnEventQueued?.Invoke(q.Peek()); await currentTransitionLoop; - currentTransitionLoop = eventQueue.Loop(async e => currentState = await Transition(e)); + currentTransitionLoop = EventQueue.Loop(async e => currentState = await Transition(e)); } private async Task Transition(IEvent e) { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs index 7346345d9..7134c2891 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs @@ -3,8 +3,8 @@ namespace PubnubApi.EventEngine.Subscribe.Context { public class ReconnectionConfiguration { - public PNReconnectionPolicy ReconnectionPolicy { get; set; } = PNReconnectionPolicy.NONE; - public int MaximumReconnectionRetries; + public PNReconnectionPolicy ReconnectionPolicy { get; set; } + public int MaximumReconnectionRetries { get; set; } public ReconnectionConfiguration(PNReconnectionPolicy policy, int maximumReconnectionRetries) { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs index fb651aaae..393002481 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs @@ -23,6 +23,7 @@ public static int CalculateDelay(PNReconnectionPolicy policy, int attempts) 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 index af2d1f10f..e946b5c4a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs @@ -21,7 +21,7 @@ public EmitMessagesHandler(Pubnub pubnubInstance, public async override Task Run(EmitMessagesInvocation invocation) { - var processedMessages = invocation.Messages.Messages.Select(m => new PNMessageResult() + var processedMessages = invocation.Messages?.Messages.Select(m => new PNMessageResult() { Channel = m.Channel, Message = JsonConvert.DeserializeObject(m.Payload), @@ -31,10 +31,8 @@ public async override Task Run(EmitMessagesInvocation invocation) Publisher = m.IssuingClientId }); - foreach (var message in processedMessages) - { - messageEmitterFunction(pubnubInstance, message); - } + processedMessages?.ToList().ForEach(message => messageEmitterFunction(pubnubInstance, message)); + } public override bool IsBackground(EmitMessagesInvocation invocation) => false; diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index 8702d7480..a517a0601 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -51,6 +51,15 @@ public override bool IsBackground(HandshakeReconnectInvocation invocation) 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) { @@ -58,13 +67,13 @@ public override async Task Run(HandshakeInvocation invocation) eventQueue.Enqueue(new Events.HandshakeReconnectFailureEvent() { AttemptedRetries = reconnectInvocation.AttemptedRetries + 1, Status = response.Item2}); break; case Invocations.HandshakeReconnectInvocation reconnectInvocation: - eventQueue.Enqueue(new Events.HandshakeReconnectSuccessEvent() { Cursor = response.Item1, Status = response.Item2 }); + 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 = response.Item1, Status = response.Item2 }); + eventQueue.Enqueue(new Events.HandshakeSuccessEvent() { Cursor = cursor, Status = response.Item2 }); break; } @@ -76,9 +85,9 @@ public override bool IsBackground(HandshakeInvocation invocation) } - private async Task> MakeHandshakeRequest(HandshakeInvocation invocation) + private async Task> MakeHandshakeRequest(HandshakeInvocation invocation) { - var resp = await manager.HandshakeRequest( + return await manager.HandshakeRequest( PNOperationType.PNSubscribeOperation, invocation.Channels.ToArray(), invocation.ChannelGroups.ToArray(), @@ -87,21 +96,6 @@ public override bool IsBackground(HandshakeInvocation invocation) invocation.InitialSubscribeQueryParams, invocation.ExternalQueryParams ); - - try - { - var handshakeResponse = JsonConvert.DeserializeObject(resp.Item1); - var c = new SubscriptionCursor() - { - Region = handshakeResponse.Timetoken.Region, - Timetoken = handshakeResponse.Timetoken.Timestamp - }; - return new System.Tuple(c, resp.Item2); - } - catch (Exception e) - { - return new Tuple(null, new PNStatus(e, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, invocation.Channels, invocation.ChannelGroups)); - } } public override async Task Cancel() diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs index 6810ae4a2..b2aa6b3fe 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -82,26 +82,15 @@ public override bool IsBackground(ReceiveMessagesInvocation invocation) private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) { - var resp = await manager.ReceiveRequest( + return await manager.ReceiveRequest>( PNOperationType.PNSubscribeOperation, - invocation.Channels.ToArray(), - invocation.ChannelGroups.ToArray(), + invocation.Channels?.ToArray(), + invocation.ChannelGroups?.ToArray(), invocation.Cursor.Timetoken.Value, invocation.Cursor.Region.Value, invocation.InitialSubscribeQueryParams, invocation.ExternalQueryParams ); - - try - { - //TODO: get ReceivingResponse from manager.ReceiveRequest - var receiveResponse = JsonConvert.DeserializeObject>(resp.Item1); - return new System.Tuple, PNStatus>(receiveResponse, resp.Item2); - } - catch (Exception e) - { - return new Tuple, PNStatus>(null, new PNStatus(e, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, invocation.Channels, invocation.ChannelGroups)); - } } public override async Task Cancel() diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs index 3a3c1de25..da88ecc14 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -1,29 +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(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); + var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, EventQueue); dispatcher.Register(handshakeHandler); - dispatcher.Register(handshakeHandler); dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeHandler); - var receiveHandler = new Effects.ReceivingEffectHandler(subscribeManager, eventQueue); + var receiveHandler = new Effects.ReceivingEffectHandler(subscribeManager, EventQueue); dispatcher.Register(receiveHandler); - dispatcher.Register(receiveHandler); dispatcher.Register(receiveHandler); + dispatcher.Register(receiveHandler); + dispatcher.Register(receiveHandler); - // TODO: ReconnectionConfiguration - currentState = new UnsubscribedState(); + 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/Model/Consumer/PNStatus.cs b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs index aceb340e7..2c0d34eb3 100644 --- a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs +++ b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs @@ -16,12 +16,16 @@ public PNStatus() { } public PNStatus(Exception e, PNOperationType operationType, PNStatusCategory category, IEnumerable affectedChannels = null, IEnumerable affectedChannelGroups = null) { - this.Error = true; + 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) diff --git a/src/Api/PubnubApi/Pubnub.cs b/src/Api/PubnubApi/Pubnub.cs index 6c28c0013..dbbbd2a27 100644 --- a/src/Api/PubnubApi/Pubnub.cs +++ b/src/Api/PubnubApi/Pubnub.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Globalization; using System.Reflection; +using PubnubApi.EventEngine.Subscribe; +using PubnubApi.EndPoint; +using PubnubApi.EventEngine.Subscribe.Events; #if !NET35 && !NET40 using System.Collections.Concurrent; #endif @@ -18,7 +21,8 @@ public class Pubnub private readonly EndPoint.TokenManager tokenManager; private object savedSubscribeOperation; private readonly string savedSdkVerion; - private List subscribeCallbackListenerList + private SubscribeEventEngineFactory subscribeEventEngineFactory; + private List subscribeCallbackListenerList { get; set; @@ -42,10 +46,11 @@ public ISubscribeOperation Subscribe() { if (pubnubConfig[InstanceId].EnableEventEngine) { - EndPoint.SubscribeOperation2 subscribeOperation = new EndPoint.SubscribeOperation2(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); + 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; + + //subscribeOperation.CurrentPubnubInstance(this); + savedSubscribeOperation = subscribeOperation; return subscribeOperation; } else @@ -56,6 +61,7 @@ public ISubscribeOperation Subscribe() return subscribeOperation; } } + public EndPoint.UnsubscribeOperation Unsubscribe() { EndPoint.UnsubscribeOperation unsubscribeOperation = new EndPoint.UnsubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, this); @@ -885,7 +891,9 @@ public Pubnub(PNConfiguration config) { savedSdkVerion = Version; InstanceId = Guid.NewGuid().ToString(); + subscribeEventEngineFactory = new SubscribeEventEngineFactory(); pubnubConfig.AddOrUpdate(InstanceId, config, (k, o) => config); + if (config != null) { pubnubLog = config.PubnubLog; @@ -954,6 +962,6 @@ private void CheckRequiredConfigValues() } } -#endregion + #endregion } } \ No newline at end of file diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index bc14ab3e8..55134d8f3 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -232,6 +232,8 @@ + + @@ -246,6 +248,7 @@ + HttpUtility\HttpUtility.cs diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 59b40e332..5cb01b762 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -349,6 +349,8 @@ + + @@ -363,6 +365,7 @@ + HttpUtility\HttpUtility.cs diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs index f65a31685..d60b61302 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeFailedStateTransition.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Events; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs index 85c4360cc..9d214e61a 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Context; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs index 848403a57..50e3fafd5 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Context; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs index fd92a2209..6147eec88 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Context;