diff --git a/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs b/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs index 135ec60c7..372f620a7 100644 --- a/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs +++ b/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs @@ -85,6 +85,11 @@ Uri IUrlRequestBuilder.BuildMultiChannelSubscribeRequest(string requestMethod, s requestQueryStringParams.Add("filter-expr", UriUtil.EncodeUriComponent(pubnubConfig[pubnubInstanceId].FilterExpression, currentType, false, false, false)); } + if (!requestQueryStringParams.ContainsKey("ee") && pubnubConfig.ContainsKey(pubnubInstanceId) && pubnubConfig[pubnubInstanceId].EnableEventEngine) + { + requestQueryStringParams.Add("ee", "1"); + } + if (!requestQueryStringParams.ContainsKey("tt")) { requestQueryStringParams.Add("tt", timetoken.ToString(CultureInfo.InvariantCulture)); diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs new file mode 100644 index 000000000..caf39d606 --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Globalization; +using PubnubApi.EventEngine.Subscribe; +using PubnubApi; +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.States; +using PubnubApi.EventEngine.Subscribe.Common; + +namespace PubnubApi.EndPoint +{ + public class SubscribeEndpoint: ISubscribeOperation + { + private readonly PNConfiguration config; + private readonly IJsonPluggableLibrary jsonLibrary; + private readonly IPubnubUnitTest unit; + private readonly IPubnubLog pubnubLog; + private readonly EndPoint.TelemetryManager pubnubTelemetryMgr; + private readonly EndPoint.TokenManager pubnubTokenMgr; + + private List subscribeChannelNames = new List(); + private List subscribeChannelGroupNames = new List(); + private long subscribeTimetoken = -1; + private bool presenceSubscribeEnabled; + private SubscribeManager2 manager; + private Dictionary queryParam; + private Pubnub PubnubInstance; + private SubscribeEventEngine subscribeEventEngine; + private SubscribeEventEngineFactory subscribeEventEngineFactory { get; set; } + private string instanceId { get; set; } + public List SubscribeListenerList + { + get; + set; + } = new List(); + + public SubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager,SubscribeEventEngineFactory subscribeEventEngineFactory, string instanceId, Pubnub instance) + { + PubnubInstance = instance; + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + pubnubTokenMgr = tokenManager; + this.subscribeEventEngineFactory = subscribeEventEngineFactory; + this.instanceId = instanceId; + if (unit != null) { unit.EventTypeList = new List>(); } + } + + public ISubscribeOperation Channels(string[] channels) + { + if (channels != null && channels.Length > 0 && !string.IsNullOrEmpty(channels[0])) + { + this.subscribeChannelNames.AddRange(channels); + } + return this; + } + + public ISubscribeOperation ChannelGroups(string[] channelGroups) + { + if (channelGroups != null && channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0])) + { + this.subscribeChannelGroupNames.AddRange(channelGroups); + } + return this; + } + + public ISubscribeOperation WithTimetoken(long timetoken) + { + this.subscribeTimetoken = timetoken; + return this; + } + + public ISubscribeOperation WithPresence() + { + this.presenceSubscribeEnabled = true; + return this; + } + + public ISubscribeOperation QueryParam(Dictionary customQueryParam) + { + this.queryParam = customQueryParam; + return this; + } + + public void Execute() + { + if (this.subscribeChannelNames == null) + { + this.subscribeChannelNames = new List(); + } + + if (this.subscribeChannelGroupNames == null) + { + this.subscribeChannelGroupNames = new List(); + } + + if (this.presenceSubscribeEnabled) + { + List presenceChannelNames = (this.subscribeChannelNames != null && this.subscribeChannelNames.Count > 0 && !string.IsNullOrEmpty(this.subscribeChannelNames[0])) + ? this.subscribeChannelNames.Select(c => string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", c)).ToList() : new List(); + List presenceChannelGroupNames = (this.subscribeChannelGroupNames != null && this.subscribeChannelGroupNames.Count > 0 && !string.IsNullOrEmpty(this.subscribeChannelGroupNames[0])) + ? this.subscribeChannelGroupNames.Select(c => string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", c)).ToList() : new List(); + + if (this.subscribeChannelNames != null && presenceChannelNames.Count > 0) + { + this.subscribeChannelNames.AddRange(presenceChannelNames); + } + + if (this.subscribeChannelGroupNames != null && presenceChannelGroupNames.Count > 0) + { + this.subscribeChannelGroupNames.AddRange(presenceChannelGroupNames); + } + } + + string[] channelNames = this.subscribeChannelNames != null ? this.subscribeChannelNames.ToArray() : null; + string[] channelGroupNames = this.subscribeChannelGroupNames != null ? this.subscribeChannelGroupNames.ToArray() : null; + SubscriptionCursor cursor = null; + if (subscribeTimetoken >= 1) + { + cursor = new SubscriptionCursor { Timetoken = subscribeTimetoken, Region = 0 }; + } + Subscribe(channelNames, channelGroupNames, cursor, this.queryParam); + } + + private void Subscribe(string[] channels, string[] channelGroups, SubscriptionCursor cursor, 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."); + } + + if (this.subscribeEventEngineFactory.HasEventEngine(instanceId)) + { + subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(instanceId); + } + else + { + var subscribeManager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); + subscribeEventEngine = subscribeEventEngineFactory.InitializeEventEngine(instanceId, PubnubInstance, config, subscribeManager, StatusEmitter, MessageEmitter); + subscribeEventEngine.OnStateTransition += SubscribeEventEngine_OnStateTransition; + subscribeEventEngine.OnEventQueued += SubscribeEventEngine_OnEventQueued; + subscribeEventEngine.OnEffectDispatch += SubscribeEventEngine_OnEffectDispatch; + } + subscribeEventEngine.Subscribe(channels, channelGroups, cursor); + } + + private void SubscribeEventEngine_OnEffectDispatch(IEffectInvocation obj) + { + try + { + unit?.EventTypeList.Add(new KeyValuePair("invocation", obj?.Name)); + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, EE OnEffectDispatch : CurrentState = {subscribeEventEngine.CurrentState.GetType().Name} => Invocation = {obj.GetType().Name}", config.LogVerbosity); + } + catch (Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, EE OnEffectDispatch : CurrentState = {subscribeEventEngine.CurrentState.GetType().Name} => EXCEPTION = {ex}", config.LogVerbosity); + } + } + + private void SubscribeEventEngine_OnEventQueued(IEvent @event) + { + try + { + unit?.EventTypeList.Add(new KeyValuePair("event", @event?.Name)); + int attempts = 0; + if (subscribeEventEngine.CurrentState is HandshakeReconnectingState handshakeReconnectingState) + { + attempts = handshakeReconnectingState.AttemptedRetries; + } + else if (subscribeEventEngine.CurrentState is ReceiveReconnectingState receiveReconnectingState) + { + attempts = receiveReconnectingState.AttemptedRetries; + } + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, EE OnEventQueued : CurrentState: {subscribeEventEngine.CurrentState.GetType().Name}; Event = {@event.GetType().Name}; Attempt = {attempts} of {config.ConnectionMaxRetries}", config.LogVerbosity); + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, EE OnEventQueued : CurrentState = {subscribeEventEngine.CurrentState.GetType().Name} => EXCEPTION = {ex}", config.LogVerbosity); + } + } + + private void SubscribeEventEngine_OnStateTransition(EventEngine.Core.TransitionResult obj) + { + try + { + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, EE OnStateTransition : CurrentState = {subscribeEventEngine.CurrentState.GetType().Name} => Transition State = {obj?.State.GetType().Name}", config.LogVerbosity); + } + catch(Exception ex) + { + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, EE OnStateTransition : CurrentState = {subscribeEventEngine.CurrentState.GetType().Name} => EXCEPTION = {ex}", config.LogVerbosity); + } + } + + private void MessageEmitter(Pubnub pubnubInstance, PNMessageResult messageResult) + { + foreach (var listener in SubscribeListenerList) + { + listener?.Message(pubnubInstance, messageResult); + } + } + + private void StatusEmitter(Pubnub pubnubInstance, PNStatus status) + { + foreach (var listener in SubscribeListenerList) + { + listener?.Status(pubnubInstance, status); + } + } + + } +} diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs index a84db0422..e8bd852a6 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs @@ -8,6 +8,7 @@ using System.Collections; using System.Text; using PubnubApi.EventEngine.Subscribe.Common; +using Newtonsoft.Json; #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 using System.Net.Http; using System.Net.Http.Headers; @@ -112,7 +113,7 @@ public async Task> HandshakeRequest(PNOperati if (!string.IsNullOrEmpty(responseTuple.Item1) && responseTuple.Item2 == null) { PNStatus status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, channels, channelGroups); - HandshakeResponse handshakeResponse = jsonLibrary.DeserializeToObject(responseTuple.Item1); + HandshakeResponse handshakeResponse = JsonConvert.DeserializeObject(responseTuple.Item1); return new Tuple(handshakeResponse, status); } @@ -143,9 +144,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, PNStatus>> 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, PNStatus> resp = new Tuple, PNStatus>(null, null); + Tuple, PNStatus> resp = new Tuple, PNStatus>(null, null); try { @@ -154,7 +155,7 @@ internal async Task, PNStatus>> ReceiveRequest> pubnubRequestState = new RequestState>(); + RequestState> pubnubRequestState = new RequestState>(); pubnubRequestState.Channels = channels; pubnubRequestState.ChannelGroups = channelGroups; pubnubRequestState.ResponseType = responseType; @@ -168,10 +169,14 @@ internal async Task, PNStatus>> ReceiveRequest receiveResponse = jsonLibrary.DeserializeToObject>(responseTuple.Item1); - return new Tuple, PNStatus>(receiveResponse, status); - } - return new Tuple, PNStatus>(null, responseTuple.Item2); + ReceivingResponse receiveResponse = JsonConvert.DeserializeObject>(responseTuple.Item1); + return new Tuple, PNStatus>(receiveResponse, status); + } + else if (responseTuple.Item2 != null) + { + return new Tuple, PNStatus>(null, responseTuple.Item2); + } + return new Tuple, PNStatus>(null, new PNStatus(new Exception("ReceiveRequest failed."), PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, channels, channelGroups)); } catch(Exception ex) { @@ -305,7 +310,10 @@ internal protected async Task> UrlProcessRequest(Uri jsonString = await pubnubHttp.SendRequestAndGetJsonResponse(requestUri, pubnubRequestState, request).ConfigureAwait(false); } #endif - + if (pubnubLog != null && config != null) + { + LoggingMethod.WriteToLog(pubnubLog, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, JSON= {jsonString} for request={requestUri}", config.LogVerbosity); + } PNStatus errStatus = GetStatusIfError(pubnubRequestState, jsonString); return new Tuple((errStatus == null) ? jsonString : "", errStatus); } @@ -509,6 +517,11 @@ private PNStatus GetStatusIfError(RequestState asyncRequestState, string j PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(statusCode, statusMessage); status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, asyncRequestState, statusCode, new PNException(jsonString)); } + else if (statusCode != 200) + { + PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(statusCode, errorMessageJson); + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, asyncRequestState, statusCode, new PNException(jsonString)); + } } else if (deserializeStatus.Count >= 1 && deserializeStatus.ContainsKey("status") && string.Equals(deserializeStatus["status"].ToString(), "error", StringComparison.OrdinalIgnoreCase) && deserializeStatus.ContainsKey("error")) { @@ -549,62 +562,14 @@ private PNStatus GetStatusIfError(RequestState asyncRequestState, string j { status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, PNStatusCategory.PNNetworkIssuesCategory, asyncRequestState, (int)HttpStatusCode.NotFound, new PNException(jsonString)); } - - return status; - } - - internal List WrapResultBasedOnResponseType(PNOperationType type, string jsonString, string[] channels, string[] channelGroups) - { - List result = new List(); - try + else if (!NewtonsoftJsonDotNet.JsonFastCheck(jsonString)) { - string multiChannel = (channels != null) ? string.Join(",", channels.OrderBy(x => x).ToArray()) : ""; - string multiChannelGroup = (channelGroups != null) ? string.Join(",", channelGroups.OrderBy(x => x).ToArray()) : ""; - - if (!string.IsNullOrEmpty(jsonString)) - { - object deserializedResult = jsonLibrary.DeserializeToObject(jsonString); - List result1 = ((IEnumerable)deserializedResult).Cast().ToList(); - - if (result1 != null && result1.Count > 0) - { - result = result1; - } - - switch (type) - { - case PNOperationType.PNSubscribeOperation: - case PNOperationType.Presence: - if (result.Count == 3 && result[0] is object[] && (result[0] as object[]).Length == 0 && result[2].ToString() == "") - { - result.RemoveAt(2); - } - if (result.Count == 4 && result[0] is object[] && (result[0] as object[]).Length == 0 && result[2].ToString() == "" && result[3].ToString() == "") - { - result.RemoveRange(2, 2); - } - result.Add(multiChannelGroup); - result.Add(multiChannel); - - break; - case PNOperationType.PNHeartbeatOperation: - //Dictionary heartbeatadictionary = jsonLibrary.DeserializeToDictionaryOfObject(jsonString); - //result = new List(); - //result.Add(heartbeatadictionary); - //result.Add(multiChannel); - break; - default: - break; - } - //switch stmt end - } + status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, PNStatusCategory.PNNetworkIssuesCategory, asyncRequestState, (int)HttpStatusCode.NotFound, new PNException(jsonString)); } - catch { /* ignore */ } - return result; + return status; } - internal bool Disconnect() { return true; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs deleted file mode 100644 index 2a4f7282f..000000000 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ /dev/null @@ -1,769 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Net; -using System.Globalization; -using PubnubApi.PubnubEventEngine; -using PubnubApi.EventEngine.Subscribe; -using PubnubApi; - -namespace PubnubApi.EndPoint -{ - public class SubscribeOperation2: ISubscribeOperation - { - private readonly PNConfiguration config; - private readonly IJsonPluggableLibrary jsonLibrary; - private readonly IPubnubUnitTest unit; - private readonly IPubnubLog pubnubLog; - private readonly EndPoint.TelemetryManager pubnubTelemetryMgr; - private readonly EndPoint.TokenManager pubnubTokenMgr; - - private List subscribeChannelNames = new List(); - private List subscribeChannelGroupNames = new List(); - private long subscribeTimetoken = -1; - private bool presenceSubscribeEnabled; - private SubscribeManager2 manager; - private Dictionary queryParam; - private Pubnub PubnubInstance; - private SubscribeEventEngine subscribeEventEngine; - public SubscribeEventEngineFactory subscribeEventEngineFactory; - public string instanceId; - public List SubscribeListenerList - { - get; - set; - } = new List(); - - public SubscribeOperation2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager,SubscribeEventEngineFactory subscribeEventEngineFactory, string instanceId, Pubnub instance) - { - PubnubInstance = instance; - config = pubnubConfig; - jsonLibrary = jsonPluggableLibrary; - unit = pubnubUnit; - pubnubLog = log; - pubnubTelemetryMgr = telemetryManager; - pubnubTokenMgr = tokenManager; - this.subscribeEventEngineFactory = subscribeEventEngineFactory; - this.instanceId = instanceId; - - var eventEmitter = new EventEmitter(); - eventEmitter.RegisterJsonListener(JsonCallback); - - // pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); - // pnEventEngine.PubnubUnitTest = unit; - // pnEventEngine.Setup(config); - - // if (pnEventEngine.PubnubUnitTest != null) - // { - // pnEventEngine.PubnubUnitTest.EventTypeList = new List>(); - // } - // else - // { - //pnEventEngine.InitialState(new State(StateType.Unsubscribed) { EventType = EventType.SubscriptionChanged }); - // } - } - - private void HandshakeEffect_CancelHandshakeRequested(object sender, CancelHandshakeRequestEventArgs e) - { - manager.HandshakeRequestCancellation(); - } - private void HandshakeReconnectEffect_CancelHandshakeRequested(object sender, CancelHandshakeReconnectRequestEventArgs e) - { - manager.HandshakeRequestCancellation(); - } - private void ReceivingEffect_CancelReceiveRequested(object sender, CancelReceiveRequestEventArgs e) - { - manager.ReceiveRequestCancellation(); - } - private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, CancelReceiveReconnectRequestEventArgs e) - { - manager.ReceiveReconnectRequestCancellation(); - } - - private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) - { - } - - protected void ProcessListenerCallback(List result, bool zeroTimeTokenRequest, int messageCount, string[] channels, string[] channelGroups) - { - bool callbackAvailable = false; - if (result != null && result.Count >= 1 && SubscribeListenerList.Count >= 1) - { - callbackAvailable = true; - } - if (callbackAvailable) - { - if (zeroTimeTokenRequest) - { - ResponseToConnectCallback(PNOperationType.PNSubscribeOperation, channels, channelGroups); - } - else if (messageCount > 0) - { - ResponseToUserCallback(result, PNOperationType.PNSubscribeOperation); - } - } - } - - private void ResponseToConnectCallback(PNOperationType type, string[] channels, string[] channelGroups) - { - StatusBuilder statusBuilder = new StatusBuilder(config, jsonLibrary); - PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNConnectedCategory, null, (int)HttpStatusCode.OK, null); - - Announce(status); - } - - internal void Announce(PNStatus status) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].Status(PubnubInstance, status); - } - } - - internal void Announce(PNMessageResult message) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].Message(PubnubInstance, message); - } - } - - internal void Announce(PNSignalResult message) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].Signal(PubnubInstance, message); - } - } - - internal void Announce(PNFileEventResult message) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].File(PubnubInstance, message); - } - } - - internal void Announce(PNPresenceEventResult presence) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].Presence(PubnubInstance, presence); - } - } - - internal void Announce(PNObjectEventResult objectApiEvent) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].ObjectEvent(PubnubInstance, objectApiEvent); - } - } - - internal void Announce(PNMessageActionEventResult messageActionEvent) - { - List callbackList = SubscribeListenerList; - for (int listenerIndex = 0; listenerIndex < callbackList.Count; listenerIndex++) - { - callbackList[listenerIndex].MessageAction(PubnubInstance, messageActionEvent); - } - } - - private void ResponseToUserCallback(List result, PNOperationType type) - { - IPubnubLog currentLog = null; - try - { - switch (type) - { - case PNOperationType.PNSubscribeOperation: - case PNOperationType.Presence: - List messageList = GetMessageFromMultiplexResult(result); - if (messageList != null && messageList.Count > 0) - { - if (messageList.Count >= config.RequestMessageCountThreshold) - { - StatusBuilder statusBuilder = new StatusBuilder(config, jsonLibrary); - PNStatus status = statusBuilder.CreateStatusResponse(type, PNStatusCategory.PNRequestMessageCountExceededCategory, null, (int)HttpStatusCode.OK, null); - Announce(status); - } - - if (config != null && currentLog != null) - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList.Count = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageList.Count), config.LogVerbosity); - } - for (int messageIndex = 0; messageIndex < messageList.Count; messageIndex++) - { - SubscribeMessage currentMessage = messageList[messageIndex]; - if (currentMessage != null) - { - if (config != null && currentLog != null && config.DedupOnSubscribe && IsTargetForDedup(currentMessage)) - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList for loop - messageIndex = {1} => IsTargetForDedup", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageIndex), config.LogVerbosity); - continue; - } - - string currentMessageChannel = currentMessage.Channel; - string currentMessageChannelGroup = currentMessage.SubscriptionMatch; - - if (currentMessageChannel.Replace("-pnpres", "") == currentMessageChannelGroup.Replace("-pnpres", "")) - { - currentMessageChannelGroup = ""; - } - - object payload = currentMessage.Payload; - - List payloadContainer = new List(); //First item always message - if (currentMessageChannel.Contains("-pnpres") || currentMessageChannel.Contains(".*-pnpres")) - { - payloadContainer.Add(payload); - } - else if (currentMessage.MessageType == 2) //Objects Simplification events - { - double objectsVersion = -1; - Dictionary objectsDic = payload as Dictionary; - if (objectsDic != null - && objectsDic.ContainsKey("source") && objectsDic.ContainsKey("version") - && objectsDic["source"].ToString() == "objects" && Double.TryParse(objectsDic["version"].ToString(), out objectsVersion)) - { - if (objectsVersion.CompareTo(2D) == 0) //Process only version=2 for Objects Simplification. Ignore 1. - { - payloadContainer.Add(payload); - } - else - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - Legacy Objects V1. Ignoring this.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); - continue; - } - } - else - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - MessageType =2 but NOT valid format to process", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); - continue; - } - } - else - { - if (config.CipherKey.Length > 0 && currentMessage.MessageType != 1) //decrypt the subscriber message if cipherkey is available - { - string decryptMessage = ""; - PubnubCrypto aes = new PubnubCrypto(config.CipherKey, config, currentLog, null); - try - { - decryptMessage = aes.Decrypt(payload.ToString()); - } - catch (Exception ex) - { - decryptMessage = "**DECRYPT ERROR**"; - - PNStatusCategory category = PNStatusCategoryHelper.GetPNStatusCategory(ex); - PNStatus status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(type, category, null, (int)HttpStatusCode.NotFound, new PNException(ex)); - if (!string.IsNullOrEmpty(currentMessageChannel)) - { - status.AffectedChannels.Add(currentMessageChannel); - status.AffectedChannels = status.AffectedChannels.Distinct().ToList(); - } - if (!string.IsNullOrEmpty(currentMessageChannelGroup)) - { - status.AffectedChannelGroups.Add(currentMessageChannelGroup); - status.AffectedChannelGroups = status.AffectedChannelGroups.Distinct().ToList(); - } - - Announce(status); - } - object decodeMessage = (decryptMessage == "**DECRYPT ERROR**") ? decryptMessage : jsonLibrary.DeserializeToObject(decryptMessage); - - payloadContainer.Add(decodeMessage); - } - else - { - string payloadJson = jsonLibrary.SerializeToJsonString(payload); - object payloadJObject = jsonLibrary.BuildJsonObject(payloadJson); - if (payloadJObject == null) - { - payloadContainer.Add(payload); - } - else - { - payloadContainer.Add(payloadJObject); - } - } - } - - object userMetaData = currentMessage.UserMetadata; - - payloadContainer.Add(userMetaData); //Second one always user meta data - - payloadContainer.Add(currentMessage.PublishTimetokenMetadata.Timetoken); //Third one always Timetoken - - payloadContainer.Add(currentMessage.IssuingClientId); //Fourth one always Publisher - - if (!string.IsNullOrEmpty(currentMessageChannelGroup)) //Add cg first before channel - { - payloadContainer.Add(currentMessageChannelGroup); - } - - if (!string.IsNullOrEmpty(currentMessageChannel)) - { - payloadContainer.Add(currentMessageChannel); - } - - if (currentMessage.MessageType == 1) - { - ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); - PNMessageResult pnMessageResult = responseBuilder.JsonToObject>(payloadContainer, true); - if (pnMessageResult != null) - { - PNSignalResult signalMessage = new PNSignalResult - { - Channel = pnMessageResult.Channel, - Message = pnMessageResult.Message, - Subscription = pnMessageResult.Subscription, - Timetoken = pnMessageResult.Timetoken, - UserMetadata = pnMessageResult.UserMetadata, - Publisher = pnMessageResult.Publisher - }; - Announce(signalMessage); - } - } - else if (currentMessage.MessageType == 2) - { - ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); - PNObjectEventResult objectApiEvent = responseBuilder.JsonToObject(payloadContainer, true); - if (objectApiEvent != null) - { - Announce(objectApiEvent); - } - } - else if (currentMessage.MessageType == 3) - { - ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); - PNMessageActionEventResult msgActionEventEvent = responseBuilder.JsonToObject(payloadContainer, true); - if (msgActionEventEvent != null) - { - Announce(msgActionEventEvent); - } - } - else if (currentMessage.MessageType == 4) - { - ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); - PNMessageResult pnFileResult = responseBuilder.JsonToObject>(payloadContainer, true); - if (pnFileResult != null) - { - PNFileEventResult fileMessage = new PNFileEventResult - { - Channel = pnFileResult.Channel, - Subscription = pnFileResult.Subscription, - Timetoken = pnFileResult.Timetoken, - Publisher = pnFileResult.Publisher, - }; - Dictionary pnMsgObjDic = JsonDataParseInternalUtil.ConvertToDictionaryObject(pnFileResult.Message); - if (pnMsgObjDic != null && pnMsgObjDic.Count > 0) - { - if (pnMsgObjDic.ContainsKey("message") && pnMsgObjDic["message"] != null) - { - fileMessage.Message = pnMsgObjDic["message"]; - } - if (pnMsgObjDic.ContainsKey("file")) - { - Dictionary fileObjDic = JsonDataParseInternalUtil.ConvertToDictionaryObject(pnMsgObjDic["file"]); - if (fileObjDic != null && fileObjDic.ContainsKey("id") && fileObjDic.ContainsKey("name")) - { - fileMessage.File = new PNFile { Id = fileObjDic["id"].ToString(), Name = fileObjDic["name"].ToString() }; - IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, currentLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance.InstanceId); - Uri fileUrlRequest = urlBuilder.BuildGetFileUrlOrDeleteReqest("GET", "", fileMessage.Channel, fileMessage.File.Id, fileMessage.File.Name, null, type); - fileMessage.File.Url = fileUrlRequest.ToString(); - } - } - } - else - { - if (pnFileResult.Message != null) - { - fileMessage.Message = pnFileResult.Message; - } - } - Announce(fileMessage); - } - } - else if (currentMessageChannel.Contains("-pnpres")) - { - ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); - PNPresenceEventResult presenceEvent = responseBuilder.JsonToObject(payloadContainer, true); - if (presenceEvent != null) - { - Announce(presenceEvent); - } - } - else - { - if (config != null && currentLog != null) - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - payload = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLibrary.SerializeToJsonString(payloadContainer)), config.LogVerbosity); - } - ResponseBuilder responseBuilder = new ResponseBuilder(config, jsonLibrary, currentLog); - PNMessageResult userMessage = responseBuilder.JsonToObject>(payloadContainer, true); - if (userMessage != null) - { - Announce(userMessage); - } - } - - } - else - { - if (config != null && currentLog != null) - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - messageList for loop - messageIndex = {1} => null message", DateTime.Now.ToString(CultureInfo.InvariantCulture), messageIndex), config.LogVerbosity); - } - } - } - - } - break; - case PNOperationType.PNHeartbeatOperation: - break; - default: - break; - } - } - catch (Exception ex) - { - if (config != null && currentLog != null) - { - LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, ResponseToUserCallback - Exception = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), config.LogVerbosity); - } - } - } - - private bool IsTargetForDedup(SubscribeMessage message) - { - bool isTargetOfDedup = false; - PNConfiguration currentConfig; - IPubnubLog currentLog; - try - { - //if (pubnubSubscribeDuplicationManager.IsDuplicate(message)) - //{ - // isTargetOfDedup = true; - // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - // { - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, Dedupe - Duplicate skipped - msg = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLib.SerializeToJsonString(message)), currentConfig.LogVerbosity); - // } - //} - //else - //{ - // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - // { - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, Dedupe - AddEntry - msg = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonLib.SerializeToJsonString(message)), currentConfig.LogVerbosity); - // } - // pubnubSubscribeDuplicationManager.AddEntry(message); - //} - } - catch (Exception ex) - { - //Log and ignore any exception due to Dedupe manager - //if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - //{ - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime: {0}, IsTargetForDedup - dedupe error = {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), ex), currentConfig.LogVerbosity); - //} - } - - return isTargetOfDedup; - } - - private List GetMessageFromMultiplexResult(List result) - { - List jsonMessageList = null; - List msgList = new List(); - - Dictionary messageDicObj = jsonLibrary.ConvertToDictionaryObject(result[1]); - if (messageDicObj != null && messageDicObj.Count > 0 && messageDicObj.ContainsKey("m")) - { - jsonMessageList = messageDicObj["m"] as List; - } - else - { - messageDicObj = jsonLibrary.ConvertToDictionaryObject(result[0]); - if (messageDicObj != null && messageDicObj.Count > 0 && messageDicObj.ContainsKey("m")) - { - jsonMessageList = messageDicObj["m"] as List; - } - } - - if (jsonMessageList != null && jsonMessageList.Count > 0) - { - foreach (Dictionary dicItem in jsonMessageList) - { - if (dicItem.Count > 0) - { - SubscribeMessage msg = new SubscribeMessage(); - foreach (string key in dicItem.Keys) - { - switch (key.ToLowerInvariant()) - { - case "a": - msg.Shard = dicItem[key].ToString(); - break; - case "b": - msg.SubscriptionMatch = dicItem[key].ToString(); - break; - case "c": - msg.Channel = dicItem[key].ToString(); - break; - case "d": - msg.Payload = dicItem[key]; - break; - case "e": - int subscriptionTypeIndicator; - var _ = Int32.TryParse(dicItem[key].ToString(), out subscriptionTypeIndicator); - msg.MessageType = subscriptionTypeIndicator; - break; - case "f": - msg.Flags = dicItem[key].ToString(); - break; - case "i": - msg.IssuingClientId = dicItem[key].ToString(); - break; - case "k": - msg.SubscribeKey = dicItem[key].ToString(); - break; - case "s": - int seqNum; - _ = Int32.TryParse(dicItem[key].ToString(), out seqNum); - msg.SequenceNumber = seqNum; - break; - case "o": - Dictionary ttOriginMetaData = jsonLibrary.ConvertToDictionaryObject(dicItem[key]); - if (ttOriginMetaData != null && ttOriginMetaData.Count > 0) - { - TimetokenMetadata ttMeta = new TimetokenMetadata(); - - foreach (string metaKey in ttOriginMetaData.Keys) - { - if (metaKey.ToLowerInvariant().Equals("t", StringComparison.OrdinalIgnoreCase)) - { - long timetoken; - _ = Int64.TryParse(ttOriginMetaData[metaKey].ToString(), out timetoken); - ttMeta.Timetoken = timetoken; - } - else if (metaKey.ToLowerInvariant().Equals("r", StringComparison.OrdinalIgnoreCase)) - { - ttMeta.Region = ttOriginMetaData[metaKey].ToString(); - } - } - msg.OriginatingTimetoken = ttMeta; - } - break; - case "p": - Dictionary ttPublishMetaData = jsonLibrary.ConvertToDictionaryObject(dicItem[key]); - if (ttPublishMetaData != null && ttPublishMetaData.Count > 0) - { - TimetokenMetadata ttMeta = new TimetokenMetadata(); - - foreach (string metaKey in ttPublishMetaData.Keys) - { - string currentMetaKey = metaKey.ToLowerInvariant(); - - if (currentMetaKey.Equals("t", StringComparison.OrdinalIgnoreCase)) - { - long timetoken; - _ = Int64.TryParse(ttPublishMetaData[metaKey].ToString(), out timetoken); - ttMeta.Timetoken = timetoken; - } - else if (currentMetaKey.Equals("r", StringComparison.OrdinalIgnoreCase)) - { - ttMeta.Region = ttPublishMetaData[metaKey].ToString(); - } - } - msg.PublishTimetokenMetadata = ttMeta; - } - break; - case "u": - msg.UserMetadata = dicItem[key]; - break; - default: - break; - } - } - - msgList.Add(msg); - } - } - } - return msgList; - } - - private void LogCallback(string log) - { - LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, {1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), log), config.LogVerbosity); - } - - public ISubscribeOperation Channels(string[] channels) - { - if (channels != null && channels.Length > 0 && !string.IsNullOrEmpty(channels[0])) - { - this.subscribeChannelNames.AddRange(channels); - } - return this; - } - - public ISubscribeOperation ChannelGroups(string[] channelGroups) - { - if (channelGroups != null && channelGroups.Length > 0 && !string.IsNullOrEmpty(channelGroups[0])) - { - this.subscribeChannelGroupNames.AddRange(channelGroups); - } - return this; - } - - public ISubscribeOperation WithTimetoken(long timetoken) - { - this.subscribeTimetoken = timetoken; - return this; - } - - public ISubscribeOperation WithPresence() - { - this.presenceSubscribeEnabled = true; - return this; - } - - public ISubscribeOperation QueryParam(Dictionary customQueryParam) - { - this.queryParam = customQueryParam; - return this; - } - - public void Execute() - { - if (this.subscribeChannelNames == null) - { - this.subscribeChannelNames = new List(); - } - - if (this.subscribeChannelGroupNames == null) - { - this.subscribeChannelGroupNames = new List(); - } - - if (this.presenceSubscribeEnabled) - { - List presenceChannelNames = (this.subscribeChannelNames != null && this.subscribeChannelNames.Count > 0 && !string.IsNullOrEmpty(this.subscribeChannelNames[0])) - ? this.subscribeChannelNames.Select(c => string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", c)).ToList() : new List(); - List presenceChannelGroupNames = (this.subscribeChannelGroupNames != null && this.subscribeChannelGroupNames.Count > 0 && !string.IsNullOrEmpty(this.subscribeChannelGroupNames[0])) - ? this.subscribeChannelGroupNames.Select(c => string.Format(CultureInfo.InvariantCulture, "{0}-pnpres", c)).ToList() : new List(); - - if (this.subscribeChannelNames != null && presenceChannelNames.Count > 0) - { - this.subscribeChannelNames.AddRange(presenceChannelNames); - } - - if (this.subscribeChannelGroupNames != null && presenceChannelGroupNames.Count > 0) - { - this.subscribeChannelGroupNames.AddRange(presenceChannelGroupNames); - } - } - - string[] channelNames = this.subscribeChannelNames != null ? this.subscribeChannelNames.ToArray() : null; - string[] channelGroupNames = this.subscribeChannelGroupNames != null ? this.subscribeChannelGroupNames.ToArray() : null; - - Subscribe(channelNames, channelGroupNames, this.queryParam); - } - - private void Subscribe(string[] channels, string[] channelGroups, Dictionary externalQueryParam) - { - Action statusListener = null; - Action> messageListener = null; - if ((channels == null || channels.Length == 0) && (channelGroups == null || channelGroups.Length == 0)) - { - throw new ArgumentException("Either Channel Or Channel Group or Both should be provided."); - } - - if (this.subscribeEventEngineFactory.hasEventEngine(instanceId)) - { - subscribeEventEngine = subscribeEventEngineFactory.getEventEngine(instanceId); - } - else - { - if (SubscribeListenerList != null && SubscribeListenerList.Count > 0) { - messageListener = MessageEmitter; - statusListener = StatusEmitter; - } - var subscribeManager = new SubscribeManager2(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, pubnubTokenMgr, PubnubInstance); - subscribeEventEngine = subscribeEventEngineFactory.initializeEventEngine(instanceId, PubnubInstance, config, subscribeManager, statusListener, messageListener); - - } - subscribeEventEngine.Subscribe(channels, channelGroups); - } - - internal bool Retry(bool reconnect) - { - return false; - //if (manager == null) - //{ - // return false; - //} - - //if (reconnect) - //{ - // return manager.Reconnect(false); - //} - //else - //{ - // return manager.Disconnect(); - //} - } - - internal bool Retry(bool reconnect, bool resetSubscribeTimetoken) - { - return false; - //if (manager == null) - //{ - // return false; - //} - - //if (reconnect) - //{ - // return manager.Reconnect(resetSubscribeTimetoken); - //} - //else - //{ - // return manager.Disconnect(); - //} - } - - internal void CurrentPubnubInstance(Pubnub instance) - { - PubnubInstance = instance; - } - private void MessageEmitter(Pubnub pubnubInstance, PNMessageResult messageResult) - { - foreach (var listener in SubscribeListenerList) { - listener.Message(pubnubInstance, messageResult); - } - } - - private void StatusEmitter(Pubnub pubnubInstance, PNStatus status) - { - for(int i=0; i < SubscribeListenerList.Count; i++) - { - SubscribeCallback listener = SubscribeListenerList[i]; - if (listener != null) - { - listener.Status(pubnubInstance, status); - } - } - } - - } -} diff --git a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs new file mode 100644 index 000000000..c6585be1d --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs @@ -0,0 +1,87 @@ +using PubnubApi.EventEngine.Subscribe; +using PubnubApi.Interface; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; + +namespace PubnubApi.EndPoint +{ + public class UnsubscribeEndpoint : IUnsubscribeOperation + { + 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 string[] subscribeChannelNames; + private string[] subscribeChannelGroupNames; + private Dictionary queryParam{ get; set; } + private Pubnub pubnubInstance{ get; set; } + private SubscribeEventEngine subscribeEventEngine { get; set; } + private SubscribeEventEngineFactory subscribeEventEngineFactory { get; set; } + private string instanceId { get; set; } + + public UnsubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, SubscribeEventEngineFactory subscribeEventEngineFactory, Pubnub instance) + { + pubnubInstance = instance; + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + pubnubTokenMgr = tokenManager; + instanceId = instance.InstanceId; + } + + public IUnsubscribeOperation Channels(string[] channels) + { + this.subscribeChannelNames = channels; + return this; + } + + public IUnsubscribeOperation ChannelGroups(string[] channelGroups) + { + this.subscribeChannelGroupNames = channelGroups; + return this; + } + + public IUnsubscribeOperation QueryParam(Dictionary customQueryParam) + { + this.queryParam = customQueryParam; + return this; + } + + public void Execute() + { + Unsubscribe(subscribeChannelNames, subscribeChannelGroupNames); + } + + private void Unsubscribe(string[] channels, string[] channelGroups) + { + 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()) : ""; + + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, requested unsubscribe for channel(s)={1}, cg(s)={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), channel, channelGroup), config.LogVerbosity); + + if (this.subscribeEventEngineFactory.HasEventEngine(instanceId)) + { + subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(instanceId); + subscribeEventEngine.Unsubscribe(channels, channelGroups); + } + else + { + LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, $"DateTime {DateTime.Now.ToString(CultureInfo.InvariantCulture)}, Attempted Unsubscribe without EventEngine subscribe."), config.LogVerbosity); + } + } + } +} diff --git a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs index cb2c92594..5a6bc45ba 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs @@ -5,13 +5,14 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using PubnubApi.Interface; #if !NET35 && !NET40 using System.Collections.Concurrent; #endif namespace PubnubApi.EndPoint { - public class UnsubscribeOperation : PubnubCoreBase + public class UnsubscribeOperation : PubnubCoreBase, IUnsubscribeOperation { private readonly PNConfiguration config; private readonly IJsonPluggableLibrary jsonLibrary; @@ -34,19 +35,19 @@ public UnsubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary pubnubTokenMgr = tokenManager; } - public UnsubscribeOperation Channels(string[] channels) + public IUnsubscribeOperation Channels(string[] channels) { this.subscribeChannelNames = channels; return this; } - public UnsubscribeOperation ChannelGroups(string[] channelGroups) + public IUnsubscribeOperation ChannelGroups(string[] channelGroups) { this.subscribeChannelGroupNames = channelGroups; return this; } - public UnsubscribeOperation QueryParam(Dictionary customQueryParam) + public IUnsubscribeOperation QueryParam(Dictionary customQueryParam) { this.queryParam = customQueryParam; return this; diff --git a/src/Api/PubnubApi/EventEngine/Common/Delay.cs b/src/Api/PubnubApi/EventEngine/Common/Delay.cs index 3afb9a4a6..09b34afe1 100644 --- a/src/Api/PubnubApi/EventEngine/Common/Delay.cs +++ b/src/Api/PubnubApi/EventEngine/Common/Delay.cs @@ -6,7 +6,7 @@ namespace PubnubApi.EventEngine.Common public class Delay { public bool Cancelled { get; private set; } = false; - private readonly TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); + private TaskCompletionSource taskCompletionSource; private readonly object monitor = new object(); private readonly int milliseconds; @@ -17,17 +17,20 @@ public Delay(int milliseconds) public Task Start() { + taskCompletionSource = new TaskCompletionSource(); + Cancelled = false; #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; } + return taskCompletionSource.Task; + } public void Cancel() { + if (Cancelled) return; lock (monitor) { Cancelled = true; @@ -37,23 +40,21 @@ public void Cancel() private void AwaiterLoop() { - while(true) + lock (monitor) { - lock (monitor) + if (Cancelled) { - if (Cancelled) - { - taskCompletionSource.SetCanceled(); - break; - } - Monitor.Wait(monitor, milliseconds); - if (Cancelled) - { - taskCompletionSource.SetCanceled(); - break; - } - taskCompletionSource.SetResult(null); + taskCompletionSource.SetCanceled(); + return; } + Monitor.Wait(monitor, milliseconds); + if (Cancelled) + { + taskCompletionSource.SetCanceled(); + return; + } + taskCompletionSource.SetResult(null); + Cancelled = true; } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs b/src/Api/PubnubApi/EventEngine/Context/ReconnectionConfiguration.cs similarity index 70% rename from src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs rename to src/Api/PubnubApi/EventEngine/Context/ReconnectionConfiguration.cs index 7134c2891..43c0baabc 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionConfiguration.cs +++ b/src/Api/PubnubApi/EventEngine/Context/ReconnectionConfiguration.cs @@ -1,7 +1,11 @@ using System; -namespace PubnubApi.EventEngine.Subscribe.Context +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Context { - public class ReconnectionConfiguration + public class ReconnectionConfiguration { public PNReconnectionPolicy ReconnectionPolicy { get; set; } public int MaximumReconnectionRetries { get; set; } @@ -13,4 +17,3 @@ public ReconnectionConfiguration(PNReconnectionPolicy policy, int maximumReconne } } } - diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs b/src/Api/PubnubApi/EventEngine/Context/ReconnectionDelayUtil.cs similarity index 52% rename from src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs rename to src/Api/PubnubApi/EventEngine/Context/ReconnectionDelayUtil.cs index 393002481..d5ada3198 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Context/ReconnectionDelayUtil.cs +++ b/src/Api/PubnubApi/EventEngine/Context/ReconnectionDelayUtil.cs @@ -1,31 +1,33 @@ using System; -namespace PubnubApi.EventEngine.Subscribe.Context +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Context { - public static class ReconnectionDelayUtil + public static class ReconnectionDelayUtil { public static int CalculateDelay(PNReconnectionPolicy policy, int attempts) { - Random numGenerator = new Random(); - int delayValue = 0; - int backoff = 5; + int delayMillisecond = 0; switch (policy) { case PNReconnectionPolicy.LINEAR: - delayValue = attempts * backoff + numGenerator.Next(1000); + delayMillisecond = 3000; break; case PNReconnectionPolicy.EXPONENTIAL: - delayValue = (int)(Math.Pow(2, attempts - 1) * 1000 + numGenerator.Next(1000)); + int i = attempts % int.MaxValue; + if (attempts % 6 == 0) i = attempts + 1; + delayMillisecond = (int)((Math.Pow(2, (i % 6)) - 1) * 1000); break; } - return delayValue; - + return delayMillisecond; } 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; + return reconnectionConfiguration.MaximumReconnectionRetries >= attemptedRetries; } } } - diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs index e4fea80fc..46869f638 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs @@ -26,12 +26,17 @@ public async Task Dispatch(IEffectInvocation invocation) { { var handler = effectInvocationHandlerMap[invocation.GetType()]; if (handler.IsBackground(invocation)) - await handler.Run(invocation); + FireAndForget(handler, invocation); else await handler.Run(invocation); } } + void FireAndForget(IEffectHandler handler, IEffectInvocation invocation) + { + handler.Run(invocation); + } + /// /// Assign a handler implementation to an invocation. /// diff --git a/src/Api/PubnubApi/EventEngine/Core/Engine.cs b/src/Api/PubnubApi/EventEngine/Core/Engine.cs index 48ca50f08..91422d11f 100644 --- a/src/Api/PubnubApi/EventEngine/Core/Engine.cs +++ b/src/Api/PubnubApi/EventEngine/Core/Engine.cs @@ -1,12 +1,15 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using System.Collections.Generic; namespace PubnubApi.EventEngine.Core { public abstract class Engine { - public EventQueue EventQueue = new EventQueue(); + public readonly EventQueue EventQueue = new EventQueue(); protected EffectDispatcher dispatcher = new EffectDispatcher(); protected State currentState; + public State CurrentState => currentState; + private bool transitioning = false; private Task currentTransitionLoop = Utils.EmptyTask; @@ -42,21 +45,29 @@ public Engine() { private async void OnEvent(EventQueue q) { OnEventQueued?.Invoke(q.Peek()); - await currentTransitionLoop; - currentTransitionLoop = EventQueue.Loop(async e => currentState = await Transition(e)); + if (transitioning) return; + transitioning = true; + while (q.Count > 0) + { + await Transition(q.Dequeue()); + } + + transitioning = false; } - private async Task Transition(IEvent e) { + private async Task Transition(IEvent e) + { var stateInvocationPair = currentState.Transition(e); OnStateTransition?.Invoke(stateInvocationPair); - if (stateInvocationPair is null) { - return currentState; + if (stateInvocationPair is null) + { + return; } await ExecuteStateChange(currentState, stateInvocationPair.State, stateInvocationPair.Invocations); - return stateInvocationPair.State; + this.currentState = stateInvocationPair.State; } /// diff --git a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs index c58119d7a..e96d3bf18 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventEngineInterfaces.cs @@ -68,10 +68,10 @@ public abstract class EffectDoubleHandler : EffectHandler, IEffectHa { public new Task Run(IEffectInvocation invocation) => - invocation is T1 ? (this as EffectHandler).Run(invocation) : Run(invocation as T2); + invocation is T2 ? Run(invocation as T2) : (this as EffectHandler).Run(invocation); public new bool IsBackground(IEffectInvocation invocation) => - invocation is T1 ? (this as EffectHandler).IsBackground(invocation) : IsBackground(invocation as T2); + invocation is T2 ? IsBackground(invocation as T2) : (this as EffectHandler).IsBackground(invocation); public abstract Task Run(T2 invocation); @@ -127,6 +127,7 @@ public Task Run(T4 invocation) /// public interface IEffectInvocation { + string Name { get; set;} } /// @@ -138,6 +139,7 @@ public interface IEffectCancelInvocation : IEffectInvocation public interface IEvent { + string Name { get; set;} }; public abstract class State diff --git a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs index e22991e0b..7cf2f397d 100644 --- a/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs +++ b/src/Api/PubnubApi/EventEngine/Core/EventQueue.cs @@ -25,7 +25,7 @@ public void Enqueue(IEvent e) } } - private IEvent Dequeue() + public IEvent Dequeue() { lock (lockObj) { diff --git a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs deleted file mode 100644 index 0f94f3a99..000000000 --- a/src/Api/PubnubApi/EventEngine/EffectDispatcher.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace PubnubApi.PubnubEventEngine -{ - public class EffectDispatcher - { - public IPubnubUnitTest PubnubUnitTest { get; set; } - - private Dictionary effectInvocationActionMap; - public EffectDispatcher() - { - effectInvocationActionMap = new Dictionary(); - } - - public async void dispatch(EventType eventType,List effectInvocations, ExtendedState stateContext) - { - if (effectInvocations == null || effectInvocations.Count == 0) { return; } - foreach (var invocation in effectInvocations) { - PubnubUnitTest?.EventTypeList?.Add(new KeyValuePair("invocation", invocation.Name)); - System.Diagnostics.Debug.WriteLine("Found effect " + invocation.Effectype); - IEffectInvocationHandler currentEffectInvocationhandler; - if (effectInvocationActionMap.TryGetValue(invocation.Effectype, out currentEffectInvocationhandler)) - { - if (invocation.IsManaged()) - { - await Task.Factory.StartNew(()=> currentEffectInvocationhandler?.Start(stateContext, eventType)).ConfigureAwait(false); - } - else if (invocation.IsCancelling()) - { - await Task.Factory.StartNew(()=> currentEffectInvocationhandler?.Cancel()).ConfigureAwait(false); - } - else - { - currentEffectInvocationhandler.Run(stateContext); - } - } - } - - } - - public void Register(EventType type, IEffectInvocationHandler handler) - { - if (effectInvocationActionMap.ContainsKey(type)) - { - throw new ArgumentException("EventType already exist"); - } - effectInvocationActionMap.Add(type, handler); - } - } -} diff --git a/src/Api/PubnubApi/EventEngine/EventEmitter.cs b/src/Api/PubnubApi/EventEngine/EventEmitter.cs deleted file mode 100644 index 1022e3665..000000000 --- a/src/Api/PubnubApi/EventEngine/EventEmitter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace PubnubApi.PubnubEventEngine -{ - public class EventEmitter - { - private Action? handler; - private Action? jsonListener; - - public void RegisterHandler(Action eventHandler) - { - this.handler = eventHandler; - } - - public void RegisterJsonListener(Action listenerHandler) - { - this.jsonListener = listenerHandler; - } - - public void emit(Event e) - { - if (handler == null) - { - throw new MissingMemberException("eventHandler is missing"); - } - this.handler(e); - } - - public void emit(string json, bool zeroTimetokenRequest, int messageCount) - { - if (jsonListener != null) - { - jsonListener(json, zeroTimetokenRequest, messageCount); - } - } - } -} diff --git a/src/Api/PubnubApi/EventEngine/EventEngine.cs b/src/Api/PubnubApi/EventEngine/EventEngine.cs deleted file mode 100644 index 5614914d9..000000000 --- a/src/Api/PubnubApi/EventEngine/EventEngine.cs +++ /dev/null @@ -1,781 +0,0 @@ -using Newtonsoft.Json; -using PubnubApi.EndPoint; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PubnubApi.PubnubEventEngine -{ - public class PubnubError : Exception - { - - } - - #region Event - public abstract class Event - { - public virtual string Name { get; set; } - public virtual EventType EventType { get; set; } - public virtual EventPayload EventPayload { get; set; } - public virtual int Attempts { get; set; } - - public Event() - { - EventPayload = new EventPayload(); - } - } - - public class SubscriptionChanged : Event - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - } - public class Disconnect : Event - { - - } - public class Reconnect : Event - { - - } - public class HandshakeSuccess : Event - { - public SubscriptionCursor SubscriptionCursor { get; set; } - } - public class SubscriptionRestored : Event - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - } - public class HandshakeFailure : Event - { - public PubnubError Reason { get; set; } - } - public class HandshakeReconnectGiveUp : Event - { - public PubnubError Reason { get; set;} - } - public class HandshakeReconnectSuccess : Event - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - } - public class HandshakeReconnectFailure : Event - { - public PubnubError Reason { get; set;} - } - public class ReceiveSuccess : Event - { - public List Messages { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - } - public class ReceiveFailure : Event - { - public PubnubError Reason { get; set;} - } - public class ReceiveReconnectFailure : Event - { - public PubnubError Reason { get; set;} - } - public class ReceiveReconnectGiveUp : Event - { - public PubnubError Reason { get; set;} - } - public class ReceiveReconnectSuccess : Event - { - public List Messages { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - } - public class Fail : Event - { - - } - public class Success : Event - { - - } - #endregion - public class EventPayload - { - public List? Channels { get; set; } - public List? ChannelGroups { get; set; } - public long? Timetoken { get; set; } - public int? Region { get; set; } - - public Exception? exception { get; set; } - } - - - public class SubscriptionCursor - { - public long? Timetoken { get; set; } - public int? Region { get; set; } - } - - #region EffectInvocation - public abstract class EffectInvocation - { - public virtual string Name { get; set; } - public virtual EventType Effectype { get; set; } - public virtual EffectInvocationType InvocationType { get; set; } - public virtual IEffectInvocationHandler Handler { get; set; } - public abstract bool IsManaged(); - public abstract bool IsCancelling(); - } - public class ReceiveMessages: EffectInvocation - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - - public override bool IsManaged() - { - return true; - } - public override bool IsCancelling() - { - return false; - } - - } - public class CancelReceiveMessages : EffectInvocation - { - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return true; - } - } - public class ReceiveReconnect: EffectInvocation - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - public int Attempts { get; set; } - public PubnubError Reason { get; set; } - public override bool IsManaged() - { - return true; - } - public override bool IsCancelling() - { - return false; - } - } - public class CancelReceiveReconnect : EffectInvocation - { - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return true; - } - } - public class Handshake : EffectInvocation - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public override bool IsManaged() - { - return true; - } - public override bool IsCancelling() - { - return false; - } - } - public class CancelHandshake : EffectInvocation - { - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return true; - } - } - public class HandshakeReconnect : EffectInvocation - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public SubscriptionCursor SubscriptionCursor { get; set; } - public int Attempts { get; set; } - public PubnubError Reason { get; set; } - public override bool IsManaged() - { - return true; - } - public override bool IsCancelling() - { - return false; - } - } - public class CancelHandshakeReconnect : EffectInvocation - { - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return true; - } - } - public class EmitStatus : EffectInvocation - { - private readonly PNStatusCategory statusCategory; - public Action AnnounceStatus { get; set; } - public EmitStatus() - { - } - public EmitStatus(PNStatusCategory status) - { - statusCategory = status; - } - - public void Announce() - { - if (Handler != null) - { - PNStatus status = Handler.GetPNStatus(); - if (AnnounceStatus != null && status != null) - { - if (Handler is ReceivingEffectHandler && status.StatusCode == 200) - { - //Ignore Announce for 200 - return; - } - else if (Handler is ReceiveReconnectingEffectHandler && status.StatusCode == 200) - { - //Ignore Announce for 200 - return; - } - System.Diagnostics.Debug.WriteLine($"Status Category = {status.Category} to be announced"); - AnnounceStatus(status); - } - } - } - - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return false; - } - } - public class EmitMessages : EffectInvocation - { - public Action LogCallback { get; set; } - //public SubscribeOperation2 SubscribeOperation { get; set; } - public Action> AnnounceMessage { get; set; } - public EmitMessages(List messages) - { - - } - public void Announce() - { - } - - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return false; - } - } - - public class HandshakeFailed : EffectInvocation - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public override bool IsManaged() - { - return true; - } - public override bool IsCancelling() - { - return false; - } - } - public class CancelHandshakeFailed : EffectInvocation - { - public override bool IsManaged() - { - return false; - } - public override bool IsCancelling() - { - return true; - } - } - #endregion - public enum EventType - { - SubscriptionChanged, - SubscriptionRestored, - Handshake, - CancelHandshake, - HandshakeSuccess, - ReceiveMessages, - CancelReceiveMessages, - ReceiveSuccess, - HandshakeFailure, - CancelHandshakeFailure, - ReceiveFailure, - ReceiveReconnect, - CancelReceiveReconnect, - ReceiveReconnectSuccess, - ReceiveReconnectFailure, - ReceiveReconnectGiveUp, - HandshakeReconnect, - CancelHandshakeReconnect, - HandshakeReconnectSuccess, - HandshakeReconnectFailure, - HandshakeReconnectGiveUp, - ReconnectionFailed, - Disconnect, - Reconnect - } - - public class ExtendedState - { - public List Channels { get; set; } - public List ChannelGroups { get; set; } - public long? Timetoken { get; set; } - public int? Region { get; set; } - public int Attempts { get; set; } - - public ExtendedState() - { - Channels = new List(); - ChannelGroups = new List(); - Timetoken = 0; - Region = 0; - Attempts = 0; - } - - } - - public class EventEngine - { - private EventEngine pnEventEngine = null; - - public ExtendedState Context; - public State? CurrentState { get; set; } - public List States { get; set; } - - public EffectDispatcher Dispatcher; - - public EventEmitter Emitter; - public IPubnubUnitTest PubnubUnitTest { get; set; } - - public EventEngine(EffectDispatcher dispatcher, EventEmitter emitter) - { - if (PubnubUnitTest != null ) - { - PubnubUnitTest.EventTypeList?.Clear(); - } - this.Dispatcher = dispatcher; - States = new List(); - Context = new ExtendedState(); - this.Emitter = emitter; - emitter.RegisterHandler(this.Transition); - } - - public State CreateState(StateType type) - { - var newState = new State(type); - if (States.Find(s=> s.StateType == type) != null) - { - throw new InvalidOperationException($"StateType = {type} already exist."); - } - States.Add(newState); - return newState; - } - - public void Transition(Event e) - { - if (CurrentState != null) { - State findState = States.Find((s) => s.StateType == CurrentState.StateType); - StateType nextStateType; - if (findState != null && findState.transitions != null && findState.transitions.TryGetValue(e.EventType, out nextStateType)) - { - System.Diagnostics.Debug.WriteLine($"Current State = {CurrentState.StateType}; Transition = {e.EventType}"); - if (PubnubUnitTest != null ) - { - PubnubUnitTest.EventTypeList.Add(new KeyValuePair("event", e.Name)); - PubnubUnitTest.Attempts = e.Attempts; - } - if (findState != null) - { - if (findState.ExitList != null && findState.ExitList.Count > 0) - { - Dispatcher.dispatch(e.EventType, findState.ExitList, this.Context); - } - if (findState.EffectInvocationsList != null - && findState.EffectInvocationsList.ContainsKey(e.EventType) - && findState.EffectInvocationsList[e.EventType].Count > 0) - { - List effectInvocationList = findState.EffectInvocationsList[e.EventType]; - if (effectInvocationList != null && effectInvocationList.Count > 0) - { - Dispatcher.dispatch(e.EventType, effectInvocationList, this.Context); - } - } - - CurrentState = States.Find((s) => s.StateType == nextStateType); - UpdateContext(e.EventType, e.EventPayload); - if (CurrentState.EntryList != null && CurrentState.EntryList.Count > 0) - { - Dispatcher.dispatch(e.EventType, CurrentState.EntryList, this.Context); - } - - UpdateContext(e.EventType, e.EventPayload); - } - - } - - } - } - - public void Subscribe(List channels, List? channelGroups) - { - var evnt = new SubscriptionChanged(); - evnt.Name = "SUBSCRIPTION_CHANGED"; - evnt.EventType = EventType.SubscriptionChanged; - evnt.EventPayload.Channels = channels; - if (channelGroups != null) evnt.EventPayload.ChannelGroups = channelGroups; - this.Transition(evnt); - } - - private void UpdateContext(EventType eventType, EventPayload eventData) - { - if (CurrentState != null) - { - CurrentState.EventType = eventType; - } - if (eventData.Channels != null) Context.Channels = eventData.Channels; - if (eventData.ChannelGroups != null) Context.ChannelGroups = eventData.ChannelGroups; - if (eventData.Timetoken != null) - { - System.Diagnostics.Debug.WriteLine($"eventData.Timetoken = {eventData.Timetoken.Value}"); - System.Diagnostics.Debug.WriteLine($"Context.Timetoken = {Context.Timetoken.Value}"); - if (Context.Timetoken > 0 && - eventType == EventType.HandshakeSuccess && - Context.Timetoken < eventData.Timetoken) - { - System.Diagnostics.Debug.WriteLine("Keeping last Context.Timetoken"); - // do not change context timetoken. We want last timetoken. - } - else - { - Context.Timetoken = eventData.Timetoken; - } - } - if (eventData.Region != null) Context.Region = eventData.Region; - } - - public void InitialState(State state) - { - this.CurrentState = state; - } - - public State NextState() - { - State nextState = null; - if (CurrentState != null) - { - StateType nextStateType; - State findState = States.Find((s) => s.StateType == CurrentState.StateType); - if (findState != null && findState.transitions != null && findState.transitions.ContainsKey(CurrentState.EventType)) - { - nextStateType = findState.transitions[CurrentState.EventType]; - nextState = States.Find((s) => s.StateType == nextStateType); - } - } - return nextState; - } - - public void Setup(PNConfiguration config) - { - - - CreateState(StateType.Unsubscribed) - .On(EventType.SubscriptionChanged, StateType.Handshaking) - .On(EventType.SubscriptionRestored, StateType.Receiving); - - #region Handshake Effect Invocations and Emit Status - EmitStatus handshakeSuccessEmitStatus = new EmitStatus(); - handshakeSuccessEmitStatus.Name = "EMIT_STATUS"; - handshakeSuccessEmitStatus.Effectype = EventType.HandshakeSuccess; - - EffectInvocation handshakeInvocation = new Handshake(); - handshakeInvocation.Name = "HANDSHAKE"; - handshakeInvocation.Effectype = EventType.Handshake; - - EffectInvocation cancelHandshakeInvocation = new CancelHandshake(); - cancelHandshakeInvocation.Name = "CANCEL_HANDSHAKE"; - cancelHandshakeInvocation.Effectype = EventType.CancelHandshake; - #endregion - #region StateType.Handshaking - CreateState(StateType.Handshaking) - .On(EventType.SubscriptionChanged, StateType.Handshaking) - .On(EventType.HandshakeSuccess, StateType.Receiving, new List() - { - handshakeSuccessEmitStatus - } - ) - .On(EventType.HandshakeFailure, StateType.HandshakeReconnecting) - .On(EventType.Disconnect, StateType.HandshakeStopped) - .On(EventType.SubscriptionRestored, StateType.Receiving) - .OnEntry(entryInvocationList: new List() - { - handshakeInvocation - } - ) - .OnExit(exitInvocationList: new List() - { - cancelHandshakeInvocation - } - ); - #endregion - - #region HandshakeReconnecting Effect Invocations and Emit Status - EmitStatus handshakeReconnectSuccessEmitStatus = new EmitStatus(); - handshakeReconnectSuccessEmitStatus.Name = "EMIT_STATUS"; - handshakeReconnectSuccessEmitStatus.Effectype = EventType.HandshakeReconnectSuccess; - - //TBD - Should we emit status/error on HandshakeReconnectFailure - //EmitStatus handshakeReconnectFailureEmitStatus = new EmitStatus(); - // handshakeReconnectFailureEmitStatus.Name = "EMIT_STATUS"; - // handshakeReconnectFailureEmitStatus.Effectype = EventType.HandshakeReconnectFailure; - - EmitStatus handshakeReconnectGiveupEmitStatus = new EmitStatus(); - handshakeReconnectGiveupEmitStatus.Name = "EMIT_STATUS"; - handshakeReconnectGiveupEmitStatus.Effectype = EventType.HandshakeReconnectGiveUp; - - EffectInvocation handshakeReconnectInvocation = new HandshakeReconnect(); - handshakeReconnectInvocation.Name = "HANDSHAKE_RECONNECT"; - handshakeReconnectInvocation.Effectype = EventType.HandshakeReconnect; - - EffectInvocation cancelHandshakeReconnectInvocation = new CancelHandshakeReconnect(); - cancelHandshakeReconnectInvocation.Name = "CANCEL_HANDSHAKE_RECONNECT"; - cancelHandshakeReconnectInvocation.Effectype = EventType.CancelHandshakeReconnect; - #endregion - #region StateType.HandshakeReconnecting - CreateState(StateType.HandshakeReconnecting) - .On(EventType.SubscriptionChanged, StateType.Handshaking) - .On(EventType.HandshakeReconnectFailure, StateType.HandshakeReconnecting) - // .On(EventType.HandshakeReconnectFailure, StateType.HandshakeReconnecting, new List() - // { - // handshakeReconnectFailureEmitStatus - // } - //) - .On(EventType.Disconnect, StateType.HandshakeStopped) - .On(EventType.HandshakeReconnectGiveUp, StateType.HandshakeFailed, new List() - { - handshakeReconnectGiveupEmitStatus - } - ) - .On(EventType.HandshakeReconnectSuccess, StateType.Receiving, new List() - { - handshakeReconnectSuccessEmitStatus - } - ) - .On(EventType.SubscriptionRestored, StateType.Receiving) - .OnEntry(entryInvocationList: new List() - { - handshakeReconnectInvocation - } - ) - .OnExit(exitInvocationList: new List() - { - cancelHandshakeReconnectInvocation - } - ); - #endregion - - #region HandshakeFailed Effect Invocations and Emit Status - EffectInvocation handshakeFailedInvocation = new HandshakeFailed(); - handshakeFailedInvocation.Name = "HANDSHAKE_FAILED"; - handshakeFailedInvocation.Effectype = EventType.HandshakeFailure; - - EffectInvocation cancelHandshakeFailedInvocation = new CancelHandshakeFailed(); - cancelHandshakeFailedInvocation.Name = "CANCEL_HANDSHAKE_FAILED"; - cancelHandshakeFailedInvocation.Effectype = EventType.CancelHandshakeFailure; - #endregion - #region StateType.HandshakeFailed - CreateState(StateType.HandshakeFailed) - .On(EventType.SubscriptionChanged, StateType.Handshaking) - .On(EventType.Reconnect, StateType.Handshaking) - .On(EventType.SubscriptionRestored, StateType.Receiving) - .OnEntry(entryInvocationList: new List() - { - handshakeFailedInvocation - } - ) - .OnExit(exitInvocationList: new List() - { - cancelHandshakeFailedInvocation - } - ); - #endregion - - #region HandshakeStopped Effect Invocations and Emit Status - #endregion - #region StateType.HandshakeStopped - CreateState(StateType.HandshakeStopped) - .On(EventType.Reconnect, StateType.Handshaking); - #endregion - - #region Receiving Effect Invocations and Emit Status - EmitStatus receiveEmitStatus = new EmitStatus(); - receiveEmitStatus.Name = "EMIT_STATUS"; - receiveEmitStatus.Effectype = EventType.ReceiveSuccess; - - EmitMessages receiveEmitMessages = new EmitMessages(null); - receiveEmitMessages.Name = "EMIT_EVENTS"; - receiveEmitMessages.Effectype = EventType.ReceiveMessages; - - EmitStatus receiveDisconnectEmitStatus = new EmitStatus(); - receiveDisconnectEmitStatus.Name = "EMIT_STATUS"; - receiveDisconnectEmitStatus.Effectype = EventType.Disconnect; - - EffectInvocation receiveMessagesInvocation = new ReceiveMessages(); - receiveMessagesInvocation.Name = "RECEIVE_EVENTS"; - receiveMessagesInvocation.Effectype = EventType.ReceiveMessages; - - EffectInvocation cancelReceiveMessages = new CancelReceiveMessages(); - cancelReceiveMessages.Name = "CANCEL_RECEIVE_EVENTS"; - cancelReceiveMessages.Effectype = EventType.CancelReceiveMessages; - #endregion - #region StateType.Receiving - CreateState(StateType.Receiving) - .On(EventType.SubscriptionChanged, StateType.Receiving) - .On(EventType.SubscriptionRestored, StateType.Receiving) - .On(EventType.ReceiveSuccess, StateType.Receiving, new List() - { - receiveEmitStatus, - receiveEmitMessages - } - ) - .On(EventType.Disconnect, StateType.ReceiveStopped, new List() - { - receiveDisconnectEmitStatus - } - ) - .On(EventType.ReceiveFailure, StateType.ReceiveReconnecting) - .OnEntry(entryInvocationList: new List() - { - receiveMessagesInvocation - } - ) - .OnExit(exitInvocationList: new List() - { - cancelReceiveMessages - } - ); - #endregion - - #region ReceiveFailed Effect Invocations and Emit Status - #endregion - #region StateType.ReceiveFailed - CreateState(StateType.ReceiveFailed) - .On(EventType.SubscriptionChanged, StateType.Receiving) - .On(EventType.SubscriptionRestored, StateType.Receiving) - .On(EventType.Reconnect, StateType.Receiving); - #endregion - - #region ReceiveReconnecting Effect Invocations and Emit Status - EmitStatus receiveReconnectEmitStatus = new EmitStatus(); - receiveReconnectEmitStatus.Name = "RECONNECT_EMIT_STATUS"; - receiveReconnectEmitStatus.Effectype = EventType.ReceiveReconnectSuccess; - - EmitMessages receiveReconnectEmitMessages = new EmitMessages(null); - receiveReconnectEmitMessages.Name = "RECEIVE_RECONNECT_EVENTS"; - receiveReconnectEmitMessages.Effectype = EventType.ReceiveMessages; - - EmitStatus receiveReconnectDisconnectEmitStatus = new EmitStatus(); - receiveReconnectDisconnectEmitStatus.Name = "RECONNECT_DISCONNECT_STATUS"; - receiveReconnectDisconnectEmitStatus.Effectype = EventType.Disconnect; - - EmitStatus receiveReconnectGiveupEmitStatus = new EmitStatus(); - receiveReconnectGiveupEmitStatus.Name = "RECONNECT_GIVEUP_STATUS"; - receiveReconnectGiveupEmitStatus.Effectype = EventType.ReceiveReconnectGiveUp; - - EffectInvocation receiveReconnectInvocation = new ReceiveReconnect(); - receiveReconnectInvocation.Name = "RECEIVE_RECONNECT"; - receiveReconnectInvocation.Effectype = EventType.ReceiveReconnect; - - EffectInvocation cancelReceiveReconnect = new CancelReceiveReconnect(); - cancelReceiveReconnect.Name = "CANCEL_RECEIVE_RECONNECT"; - cancelReceiveReconnect.Effectype = EventType.CancelReceiveReconnect; - #endregion - #region StateType.ReceiveReconnecting - CreateState(StateType.ReceiveReconnecting) - .On(EventType.SubscriptionChanged, StateType.Receiving) - .On(EventType.ReceiveReconnectFailure, StateType.ReceiveReconnecting) - .On(EventType.SubscriptionRestored, StateType.Receiving) - .On(EventType.ReceiveReconnectSuccess, StateType.Receiving, new List() - { - //receiveReconnectEmitStatus, - receiveReconnectEmitMessages - } - ) - .On(EventType.Disconnect, StateType.ReceiveStopped) - .On(EventType.ReceiveReconnectGiveUp, StateType.ReceiveFailed, new List() - { - receiveReconnectGiveupEmitStatus - } - ) - .OnEntry(entryInvocationList: new List() - { - receiveReconnectInvocation - } - ) - .OnExit(exitInvocationList: new List() - { - cancelReceiveReconnect - } - ); - #endregion - - #region ReceiveStopped Effect Invocations and Emit Status - #endregion - #region StateType.ReceiveStopped - CreateState(StateType.ReceiveStopped) - .On(EventType.Reconnect, StateType.Receiving); - #endregion - - System.Diagnostics.Debug.WriteLine("EventEngine Setup done."); - } - - //public void SetCurrentStateType(StateType stateType, EventType eventType) - //{ - // State nextState = null; - // StateType nextStateType; - // IEnumerable eventTypeStates = States.Where(s => s.StateType == stateType && s.EventType == eventType); - // foreach (State state in eventTypeStates) - // { - // if (state.transitions.ContainsKey(eventType)) - // { - // nextStateType = state.transitions[eventType]; - // } - // } - //} - } -} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs deleted file mode 100644 index a93d2326e..000000000 --- a/src/Api/PubnubApi/EventEngine/HandshakeEffectHandler.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; - - -namespace PubnubApi.PubnubEventEngine -{ - public class HandshakeResponse - { - [JsonProperty("t")] - public Timetoken Timetoken { get; set; } - - [JsonProperty("m")] - public object[] Messages { get; set; } - } - public class HandshakeError - { - [JsonProperty("status")] - public int Status { get; set; } - - [JsonProperty("error")] - public string ErrorMessage { get; set; } - } - - public class Timetoken - { - [JsonProperty("t")] - public long? Timestamp { get; set; } - - [JsonProperty("r")] - public int? Region { get; set; } - - } - - public class HandshakeRequestEventArgs : EventArgs - { - public ExtendedState ExtendedState { get; set; } - public Action HandshakeResponseCallback { get; set; } - } - public class CancelHandshakeRequestEventArgs : EventArgs - { - } - - public class HandshakeEffectHandler : IEffectInvocationHandler - { - EventEmitter emitter; - private ExtendedState extendedState { get; set;} - public Action LogCallback { get; set; } - public Action AnnounceStatus { get; set; } - private PNStatus pnStatus { get; set; } - - public event EventHandler HandshakeRequested; - public event EventHandler CancelHandshakeRequested; - protected virtual void OnHandshakeRequested(HandshakeRequestEventArgs e) - { - EventHandler handler = HandshakeRequested; - if (handler != null) - { - handler(this, e); - } - } - protected virtual void OnCancelHandshakeRequested(CancelHandshakeRequestEventArgs e) - { - EventHandler handler = CancelHandshakeRequested; - if (handler != null) - { - handler(this, e); - } - } - - CancellationTokenSource cancellationTokenSource; - public HandshakeEffectHandler(EventEmitter emitter) - { - this.emitter = emitter; - cancellationTokenSource = new CancellationTokenSource(); - } - public async void Start(ExtendedState context, EventType eventType) - { - extendedState = context; - await Task.Factory.StartNew(() => { }); - if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { - Cancel(); - } - HandshakeRequestEventArgs args = new HandshakeRequestEventArgs(); - args.ExtendedState = context; - args.HandshakeResponseCallback = OnHandshakeEffectResponseReceived; - OnHandshakeRequested(args); - } - - public void OnHandshakeEffectResponseReceived(string json) - { - try - { - LogCallback?.Invoke($"OnHandshakeEffectResponseReceived Json Response = {json}"); - var handshakeResponse = JsonConvert.DeserializeObject(json); - if (handshakeResponse != null && handshakeResponse.Timetoken != null) - { - HandshakeSuccess handshakeSuccessEvent = new HandshakeSuccess(); - handshakeSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); - handshakeSuccessEvent.SubscriptionCursor.Timetoken = handshakeResponse.Timetoken?.Timestamp; - handshakeSuccessEvent.SubscriptionCursor.Region = handshakeResponse.Timetoken?.Region; - - handshakeSuccessEvent.EventPayload.Timetoken = handshakeResponse.Timetoken?.Timestamp; - handshakeSuccessEvent.EventPayload.Region = handshakeResponse.Timetoken?.Region; - handshakeSuccessEvent.EventType = EventType.HandshakeSuccess; - handshakeSuccessEvent.Name = "HANDSHAKE_SUCCESS"; - LogCallback?.Invoke("OnHandshakeEffectResponseReceived - EventType.HandshakeSuccess"); - - pnStatus = new PNStatus(); - pnStatus.StatusCode = 200; - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNConnectedCategory; - pnStatus.Error = false; - - emitter.emit(handshakeSuccessEvent); - } - else - { - HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); - handshakeFailureEvent.Name = "HANDSHAKE_FAILURE"; - handshakeFailureEvent.EventType = EventType.HandshakeFailure; - LogCallback?.Invoke("OnHandshakeEffectResponseReceived - EventType.HandshakeFailure"); - - pnStatus = new PNStatus(); - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; - pnStatus.Error = true; - - emitter.emit(handshakeFailureEvent); - } - } - catch (Exception ex) - { - LogCallback?.Invoke($"OnHandshakeEffectResponseReceived EXCEPTION - {ex}"); - HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); - handshakeFailureEvent.Name = "HANDSHAKE_FAILURE"; - handshakeFailureEvent.EventType = EventType.HandshakeFailure; - handshakeFailureEvent.EventPayload.exception = ex; - - pnStatus = new PNStatus(); - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; - pnStatus.Error = true; - - emitter.emit(handshakeFailureEvent); - } - - //emitter.emit(json, true, 0); - } - public void Cancel() - { - if (cancellationTokenSource != null) - { - LogCallback?.Invoke($"HandshakeEffectHandler - cancellationTokenSource - cancellion attempted."); - cancellationTokenSource.Cancel(); - } - - LogCallback?.Invoke($"HandshakeEffectHandler - invoking OnCancelHandshakeRequested."); - CancelHandshakeRequestEventArgs args = new CancelHandshakeRequestEventArgs(); - OnCancelHandshakeRequested(args); - } - public void Run(ExtendedState context) - { - if (AnnounceStatus != null) - { - AnnounceStatus(pnStatus); - } - } - - public PNStatus GetPNStatus() - { - return pnStatus; - } - - } - - -} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs deleted file mode 100644 index 52100ca4a..000000000 --- a/src/Api/PubnubApi/EventEngine/HandshakeFailedEffectHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Threading; - -namespace PubnubApi.PubnubEventEngine -{ - public class HandshakeFailedEffectHandler : IEffectInvocationHandler - { - EventEmitter emitter; - //public EffectInvocationType InvocationType { get; set; } - private ExtendedState extendedState { get; set;} - private PNStatus pnStatus { get; set; } - public Action LogCallback { get; set; } - public Action AnnounceStatus { get; set; } - - CancellationTokenSource? cancellationTokenSource; - - public HandshakeFailedEffectHandler(EventEmitter emitter) - { - this.emitter = emitter; - cancellationTokenSource = new CancellationTokenSource(); - } - - public async void Start(ExtendedState context, EventType eventType) - { - extendedState = context; - await Task.Factory.StartNew(() => { }); - if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { - Cancel(); - } - - if (eventType != EventType.HandshakeReconnectGiveUp) - { - LogCallback?.Invoke("HandshakeFailedEffectHandler - EventType.Handshake"); - Reconnect reconnectEvent = new Reconnect(); - reconnectEvent.Name = "RECONNECT"; - reconnectEvent.EventType = EventType.Reconnect; - - emitter.emit(reconnectEvent); - } - - } - - public void Cancel() - { - if (cancellationTokenSource != null) - { - LogCallback?.Invoke($"HandshakeFailedEffectHandler - cancellationTokenSource - cancellion attempted."); - cancellationTokenSource.Cancel(); - } - } - public void Run(ExtendedState context) - { - if (AnnounceStatus != null) - { - AnnounceStatus(pnStatus); - } - } - - public PNStatus GetPNStatus() - { - return pnStatus; - } - } -} diff --git a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs b/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs deleted file mode 100644 index e8fa53f5c..000000000 --- a/src/Api/PubnubApi/EventEngine/HandshakeReconnectEffectHandler.cs +++ /dev/null @@ -1,274 +0,0 @@ -using Newtonsoft.Json; -using PubnubApi.PubnubEventEngine; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - - -namespace PubnubApi.PubnubEventEngine -{ - public class HandshakeReconnectRequestEventArgs : EventArgs - { - public ExtendedState ExtendedState { get; set; } - public Action HandshakeReconnectResponseCallback { get; set; } - } - public class CancelHandshakeReconnectRequestEventArgs : EventArgs - { - } - public class HandshakeReconnectEffectHandler : IEffectInvocationHandler - { - EventEmitter emitter; - private ExtendedState extendedState { get; set;} - public Action LogCallback { get; set; } - public Action AnnounceStatus { get; set; } - public PNReconnectionPolicy ReconnectionPolicy { get; set; } - public int MaxRetries { get; set; } - private PNStatus pnStatus { get; set; } - private int timerInterval; - const int MINEXPONENTIALBACKOFF = 1; - const int MAXEXPONENTIALBACKOFF = 25; - const int INTERVAL = 3; - - public event EventHandler HandshakeReconnectRequested; - public event EventHandler CancelHandshakeReconnectRequested; - System.Threading.Timer timer; - protected virtual void OnHandshakeReconnectRequested(HandshakeReconnectRequestEventArgs e) - { - EventHandler handler = HandshakeReconnectRequested; - if (handler != null) - { - handler(this, e); - } - } - protected virtual void OnCancelHandshakeReconnectRequested(CancelHandshakeReconnectRequestEventArgs e) - { - EventHandler handler = CancelHandshakeReconnectRequested; - if (handler != null) - { - handler(this, e); - } - } - - CancellationTokenSource cancellationTokenSource; - public HandshakeReconnectEffectHandler(EventEmitter emitter) - { - this.emitter = emitter; - cancellationTokenSource = new CancellationTokenSource(); - } - - public async void Start(ExtendedState context, EventType eventType) - { - extendedState = context; - await Task.Factory.StartNew(() => { }); - if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { - Cancel(); - } - - if (ReconnectionPolicy == PNReconnectionPolicy.EXPONENTIAL) - { - double numberForMath = extendedState.Attempts % 6; - timerInterval = (int)(Math.Pow(2, numberForMath) - 1); - if (timerInterval > MAXEXPONENTIALBACKOFF) - { - timerInterval = MINEXPONENTIALBACKOFF; - } - else if (timerInterval < 1) - { - timerInterval = MINEXPONENTIALBACKOFF; - } - } - else if (ReconnectionPolicy == PNReconnectionPolicy.LINEAR) - { - timerInterval = INTERVAL; - } - else - { - timerInterval = -1; - } - LogCallback?.Invoke($"HandshakeReconnectEffectHandler ReconnectionPolicy = {ReconnectionPolicy}; Interval = {timerInterval}"); - - if (timer != null) - { - timer.Change(Timeout.Infinite, Timeout.Infinite); - } - if (timerInterval != -1) - { - timer = new Timer(new TimerCallback(HandshakeReconnectTimerCallback), null, - (-1 == timerInterval) ? Timeout.Infinite : timerInterval * 1000, Timeout.Infinite); - } - else - { - PrepareFailurePNStatus(new HandshakeError() { Status = 400 }); - PrepareAndEmitHandshakeReconnectGiveupEvent(null); - } - } - - private void HandshakeReconnectTimerCallback(object state) - { - LogCallback?.Invoke("HandshakeReconnectEffectHandler Timer interval invoke"); - HandshakeReconnectRequestEventArgs args = new HandshakeReconnectRequestEventArgs(); - args.ExtendedState = extendedState; - args.HandshakeReconnectResponseCallback = OnHandshakeReconnectEffectResponseReceived; - OnHandshakeReconnectRequested(args); - } - - public void OnHandshakeReconnectEffectResponseReceived(string json) - { - try - { - LogCallback?.Invoke($"OnHandshakeReconnectEffectResponseReceived Json Response = {json}"); - var handshakeResponse = JsonConvert.DeserializeObject(json); - if (handshakeResponse != null && handshakeResponse.Timetoken != null) - { - HandshakeReconnectSuccess handshakeReconnectSuccessEvent = new HandshakeReconnectSuccess(); - handshakeReconnectSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); - handshakeReconnectSuccessEvent.SubscriptionCursor.Timetoken = handshakeResponse.Timetoken?.Timestamp; - handshakeReconnectSuccessEvent.SubscriptionCursor.Region = handshakeResponse.Timetoken?.Region; - - handshakeReconnectSuccessEvent.EventPayload.Timetoken = handshakeResponse.Timetoken?.Timestamp; - handshakeReconnectSuccessEvent.EventPayload.Region = handshakeResponse.Timetoken?.Region; - handshakeReconnectSuccessEvent.EventType = EventType.HandshakeReconnectSuccess; - handshakeReconnectSuccessEvent.Name = "HANDSHAKE_RECONNECT_SUCCESS"; - LogCallback?.Invoke("OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectSuccess"); - - pnStatus = new PNStatus(); - pnStatus.StatusCode = 200; - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNConnectedCategory; - pnStatus.Error = false; - - extendedState.Attempts = 0; - - emitter.emit(handshakeReconnectSuccessEvent); - } - else - { - var handshakeError = JsonConvert.DeserializeObject(json); - extendedState.Attempts++; - PrepareFailurePNStatus(handshakeError); - - if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectGiveUp"); - PrepareAndEmitHandshakeReconnectGiveupEvent(null); - } - else - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectFailure"); - PrepareAndEmitHandshakeReconnectFailureEvent(null); - } - } - } - catch (Exception ex) - { - extendedState.Attempts++; - PrepareFailurePNStatus(new HandshakeError() { Status = 400 }); - - if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectGiveUp"); - PrepareAndEmitHandshakeReconnectGiveupEvent(null); - - } - else - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.HandshakeReconnectFailure"); - PrepareAndEmitHandshakeReconnectFailureEvent(ex); - } - } - finally - { - if (timer != null) - { - try - { - timer.Change(Timeout.Infinite, Timeout.Infinite); - } - catch { } - } - } - } - - private void PrepareFailurePNStatus(HandshakeError error) - { - pnStatus = new PNStatus(); - pnStatus.StatusCode = (error.Status != 0) ? error.Status : 504; - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; - pnStatus.Error = true; - } - - private void PrepareAndEmitHandshakeReconnectFailureEvent(Exception ex) - { - HandshakeReconnectFailure handshakeReconnectFailureEvent = new HandshakeReconnectFailure(); - handshakeReconnectFailureEvent.Name = "HANDSHAKE_RECONNECT_FAILURE"; - handshakeReconnectFailureEvent.EventType = EventType.HandshakeReconnectFailure; - handshakeReconnectFailureEvent.Attempts = extendedState.Attempts; - if (ex != null) - { - handshakeReconnectFailureEvent.EventPayload.exception = ex; - } - - emitter.emit(handshakeReconnectFailureEvent); - } - - private void PrepareAndEmitHandshakeReconnectGiveupEvent(Exception ex) - { - HandshakeReconnectGiveUp handshakeReconnectGiveupEvent = new HandshakeReconnectGiveUp(); - handshakeReconnectGiveupEvent.Name = "HANDSHAKE_RECONNECT_GIVEUP"; - handshakeReconnectGiveupEvent.EventType = EventType.HandshakeReconnectGiveUp; - handshakeReconnectGiveupEvent.Attempts = extendedState.Attempts; - if (ex != null) - { - handshakeReconnectGiveupEvent.EventPayload.exception = ex; - } - - emitter.emit(handshakeReconnectGiveupEvent); - } - - //private void PrepareAndEmitHandshakeFailedEvent(Exception ex) - //{ - // HandshakeFailure handshakeFailureEvent = new HandshakeFailure(); - // handshakeFailureEvent.Name = "HANDSHAKE_FAILURE"; - // handshakeFailureEvent.EventType = EventType.HandshakeReconnectGiveUp; - // handshakeFailureEvent.Attempts = extendedState.Attempts; - // if (ex != null) - // { - // handshakeFailureEvent.EventPayload.exception = ex; - // } - - // emitter.emit(handshakeFailureEvent); - //} - public void Cancel() - { - if (cancellationTokenSource != null) - { - LogCallback?.Invoke($"HandshakeReconnectEffectHandler - cancellationTokenSource - cancellion attempted."); - cancellationTokenSource.Cancel(); - } - LogCallback?.Invoke($"HandshakeReconnectEffectHandler - invoking OnCancelHandshakeReconnectRequested."); - CancelHandshakeReconnectRequestEventArgs args = new CancelHandshakeReconnectRequestEventArgs(); - OnCancelHandshakeReconnectRequested(args); - } - public void Run(ExtendedState context) - { - if (AnnounceStatus != null) - { - AnnounceStatus(pnStatus); - } - } - - public PNStatus GetPNStatus() - { - return pnStatus; - } - } -} diff --git a/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs b/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs deleted file mode 100644 index 4350f3a22..000000000 --- a/src/Api/PubnubApi/EventEngine/IEffectInvocationHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace PubnubApi.PubnubEventEngine -{ - - public enum EffectInvocationType - { - Handshake, - HandshakeSuccess, - CancelHandshake, - ReceiveMessages, - ReceiveSuccess, - Disconnect, - HandshakeReconnect, - HandshakeReconnectSuccess, - CancelHandshakeReconnect, - CancelReceiveMessages, - ReceiveReconnect, - ReceiveReconnectSuccess, - ReceiveReconnectGiveUp, - CancelReceiveReconnect, - ReconnectionAttempt - } - - public interface IEffectInvocationHandler - { - void Start(ExtendedState context, EventType eventType); - void Cancel(); - void Run(ExtendedState context); - PNStatus GetPNStatus(); - } - - public interface IReceiveMessageHandler - { - Message[] GetMessages(); - } -} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Common/CommonPresenceTypes.cs b/src/Api/PubnubApi/EventEngine/Presence/Common/CommonPresenceTypes.cs new file mode 100644 index 000000000..d6f7b5c28 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Common/CommonPresenceTypes.cs @@ -0,0 +1,16 @@ +using PubnubApi.EventEngine.Context; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.Common +{ + public abstract class PresenceState : Core.State + { + public IEnumerable Channels { get; set;} + public IEnumerable ChannelGroups { get; set;} + //public SubscriptionCursor Cursor; + public ReconnectionConfiguration ReconnectionConfiguration; + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs b/src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs new file mode 100644 index 000000000..d0b8b6c42 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.Events +{ + public class JoinedEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public string Name { get; set; } = "JOINED"; + } + public class LeftEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public string Name { get; set; } = "LEFT"; + } + public class StateSetEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public string Name { get; set; } = "STATE_SET"; + } + public class LeftAllEvent : Core.IEvent { + public string Name { get; set; } = "LEFT_ALL"; + } + public class HeartbeatSuccessEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public PNStatus Status { get; set; } + public virtual string Name { get; set; } = "HEARTBEAT_SUCCESS"; + } + public class HeartbeatFailureEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public PNStatus Status { get; set; } + public virtual string Name { get; set; } = "HEARTBEAT_FAILURE"; + } + public class HeartbeatGiveUpEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public PNStatus Status { get; set; } + public virtual string Name { get; set; } + } + public class DisconnectEvent : Core.IEvent { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public string Name { get; set; } = "DISCONNECT"; + } + public class ReconnectEvent : Core.IEvent + { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public string Name { get; set; } + } + public class TimesUpEvent : Core.IEvent + { + public IEnumerable Channels{ get; set; } + public IEnumerable ChannelGroups{ get; set; } + public string Name { get; set; } = "TIMES_UP"; + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/PresenceInvocations.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/PresenceInvocations.cs new file mode 100644 index 000000000..6376229bc --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/PresenceInvocations.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.Invocations +{ + public class HeartbeatInvocation : Core.IEffectInvocation { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + //public SubscriptionCursor Cursor; + // TODO if we need these, figure out how to pass them. + public Dictionary InitialSubscribeQueryParams = new Dictionary(); + public Dictionary ExternalQueryParams = new Dictionary(); + public virtual string Name { get; set; } = "HEARTBEAT"; + } + public class CancelHeartbeatInvocation : HeartbeatInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_HANDSHAKE"; + } + + public class WaitInvocation : Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public virtual string Name { get; set; } = "WAIT"; + } + public class CancelWaitInvocation : WaitInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_WAIT"; + } + + public class LeaveInvocation : Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public virtual string Name { get; set; } = "LEAVE"; + } + + public class DelayedHeartbeatInvocation : Core.IEffectInvocation + { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public virtual string Name { get; set; } = "DELAYED_HEARTBEAT"; + } + public class CancelDelayedHeartbeatInvocation : DelayedHeartbeatInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_DELAYED_HEARTBEAT"; + } +} + diff --git a/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs new file mode 100644 index 000000000..79f900efb --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence +{ + internal class PresenceEventEngine + { + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs new file mode 100644 index 000000000..3723e71d2 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence +{ + internal class PresenceEventEngineFactory + { + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatCooldownState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatCooldownState.cs new file mode 100644 index 000000000..dc1d2828e --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatCooldownState.cs @@ -0,0 +1,64 @@ +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Presence.Invocations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.States +{ + public class HeartbeatCooldownState : PresenceState + { + public override IEnumerable OnEntry => new WaitInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); + public override IEnumerable OnExit { get; } = new CancelWaitInvocation().AsArray(); + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.LeftAllEvent leftAll => new HeartbeatInactiveState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.JoinedEvent joined => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(joined.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(joined.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.LeftEvent left => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(left.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(left.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.StateSetEvent stateSet => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(stateSet.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(stateSet.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.TimesUpEvent timesUp => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(timesUp.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(timesUp.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.DisconnectEvent disconnect => new States.HeartbeatStoppedState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(disconnect.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(disconnect.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration, + }, + + _ => null + }; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatFailedState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatFailedState.cs new file mode 100644 index 000000000..21eeb9f4e --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatFailedState.cs @@ -0,0 +1,60 @@ +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Presence.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.States +{ + public class HeartbeatFailedState : PresenceState + { + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.LeftAllEvent leftAll => new HeartbeatInactiveState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.JoinedEvent joined => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(joined.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(joined.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.LeftEvent left => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(left.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(left.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.StateSetEvent stateSet => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(stateSet.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(stateSet.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReconnectEvent reconnect => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(reconnect.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(reconnect.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration, + }, + + Events.DisconnectEvent disconnect => new States.HeartbeatStoppedState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(disconnect.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(disconnect.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration, + }, + + _ => null + }; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatInactiveState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatInactiveState.cs new file mode 100644 index 000000000..bb7a88562 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatInactiveState.cs @@ -0,0 +1,26 @@ +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Presence.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.States +{ + public class HeartbeatInactiveState : PresenceState + { + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.JoinedEvent joined => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(joined.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(joined.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + _ => null + }; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatReconnectingState.cs new file mode 100644 index 000000000..17e4bbcec --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatReconnectingState.cs @@ -0,0 +1,78 @@ +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Presence.Invocations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.States +{ + public class HeartbeatReconnectingState : PresenceState + { + public override IEnumerable OnEntry => new DelayedHeartbeatInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); + public override IEnumerable OnExit { get; } = new CancelDelayedHeartbeatInvocation().AsArray(); + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.LeftAllEvent leftAll => new HeartbeatInactiveState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HeartbeatFailureEvent heartbeatFailure => new States.HeartbeatReconnectingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(heartbeatFailure.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(heartbeatFailure.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.JoinedEvent joined => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(joined.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(joined.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.LeftEvent left => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(left.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(left.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.StateSetEvent stateSet => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(stateSet.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(stateSet.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HeartbeatSuccessEvent heartbeatSuccess => new States.HeartbeatCooldownState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(heartbeatSuccess.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(heartbeatSuccess.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HeartbeatGiveUpEvent heartbeatGiveup => new States.HeartbeatFailedState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(heartbeatGiveup.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(heartbeatGiveup.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.DisconnectEvent disconnect => new States.HeartbeatStoppedState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(disconnect.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(disconnect.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration, + }, + + _ => null + }; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatStoppedState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatStoppedState.cs new file mode 100644 index 000000000..b3f0868e0 --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatStoppedState.cs @@ -0,0 +1,53 @@ +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Presence.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.States +{ + public class HeartbeatStoppedState : PresenceState + { + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.LeftAllEvent leftAll => new HeartbeatInactiveState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.JoinedEvent joined => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(joined.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(joined.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.LeftEvent left => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(left.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(left.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.StateSetEvent stateSet => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(stateSet.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(stateSet.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.ReconnectEvent reconnect => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(reconnect.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(reconnect.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration, + }, + + _ => null + }; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatingState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatingState.cs new file mode 100644 index 000000000..82209b47a --- /dev/null +++ b/src/Api/PubnubApi/EventEngine/Presence/States/HeartbeatingState.cs @@ -0,0 +1,73 @@ +using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Presence.Common; +using PubnubApi.EventEngine.Presence.Invocations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.EventEngine.Presence.States +{ + public class HeartbeatingState : PresenceState + { + public override IEnumerable OnEntry => new HeartbeatInvocation() + { Channels = this.Channels, ChannelGroups = this.ChannelGroups }.AsArray(); + + public override IEnumerable OnExit { get; } = new CancelHeartbeatInvocation().AsArray(); + + public override TransitionResult Transition(IEvent e) + { + return e switch + { + Events.LeftAllEvent leftAll => new HeartbeatInactiveState() + { + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.JoinedEvent joined => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(joined.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(joined.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.LeftEvent left => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(left.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(left.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.StateSetEvent stateSet => new States.HeartbeatingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(stateSet.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(stateSet.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HeartbeatSuccessEvent heartbeatSuccess => new States.HeartbeatCooldownState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(heartbeatSuccess.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(heartbeatSuccess.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.HeartbeatFailureEvent heartbeatFailure => new States.HeartbeatReconnectingState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(heartbeatFailure.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(heartbeatFailure.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration + }, + + Events.DisconnectEvent disconnect => new States.HeartbeatStoppedState() + { + Channels = (Channels ?? Enumerable.Empty()).Union(disconnect.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(disconnect.ChannelGroups), + ReconnectionConfiguration = this.ReconnectionConfiguration, + }, + + _ => null + }; + } + } +} diff --git a/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs deleted file mode 100644 index 6e8872253..000000000 --- a/src/Api/PubnubApi/EventEngine/ReceiveReconnectingEffectHandler.cs +++ /dev/null @@ -1,273 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace PubnubApi.PubnubEventEngine -{ - public class ReceiveReconnectRequestEventArgs : EventArgs - { - public ExtendedState ExtendedState { get; set; } - public Action ReceiveReconnectResponseCallback { get; set; } - } - public class CancelReceiveReconnectRequestEventArgs : EventArgs - { - } - public class ReceiveReconnectingEffectHandler : IEffectInvocationHandler - { - EventEmitter eventEmitter; - private ExtendedState extendedState { get; set;} - public Action LogCallback { get; set; } - public Action AnnounceStatus { get; set; } - public PNReconnectionPolicy ReconnectionPolicy { get; set; } - public int MaxRetries { get; set; } - private Message[] receiveMessages { get; set; } - private PNStatus pnStatus { get; set; } - private int timerInterval; - const int MINEXPONENTIALBACKOFF = 1; - const int MAXEXPONENTIALBACKOFF = 25; - const int INTERVAL = 3; - - public event EventHandler ReceiveReconnectRequested; - public event EventHandler CancelReceiveReconnectRequested; - System.Threading.Timer timer; - protected virtual void OnReceiveReconnectRequested(ReceiveReconnectRequestEventArgs e) - { - EventHandler handler = ReceiveReconnectRequested; - if (handler != null) - { - handler(this, e); - } - } - protected virtual void OnCancelReceiveReconnectRequested(CancelReceiveReconnectRequestEventArgs e) - { - EventHandler handler = CancelReceiveReconnectRequested; - if (handler != null) - { - handler(this, e); - } - } - - CancellationTokenSource cancellationTokenSource; - public ReceiveReconnectingEffectHandler(EventEmitter emitter) - { - this.eventEmitter = emitter; - cancellationTokenSource = new CancellationTokenSource(); - } - - public async void Start(ExtendedState context, EventType eventType) - { - extendedState = context; - await Task.Factory.StartNew(() => { }); - if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { - Cancel(); - } - - if (ReconnectionPolicy == PNReconnectionPolicy.EXPONENTIAL) - { - double numberForMath = extendedState.Attempts % 6; - timerInterval = (int)(Math.Pow(2, numberForMath) - 1); - if (timerInterval > MAXEXPONENTIALBACKOFF) - { - timerInterval = MINEXPONENTIALBACKOFF; - } - else if (timerInterval < 1) - { - timerInterval = MINEXPONENTIALBACKOFF; - } - } - else if (ReconnectionPolicy == PNReconnectionPolicy.LINEAR) - { - timerInterval = INTERVAL; - } - else - { - timerInterval = -1; - } - LogCallback?.Invoke($"ReceiveReconnectingEffectHandler ReconnectionPolicy = {ReconnectionPolicy}; Interval = {timerInterval}"); - - if (timer != null) - { - timer.Change(Timeout.Infinite, Timeout.Infinite); - } - if (timerInterval != -1) - { - timer = new Timer(new TimerCallback(ReceiveReconnectTimerCallback), null, - (-1 == timerInterval) ? Timeout.Infinite : timerInterval * 1000, Timeout.Infinite); - } - else - { - PrepareFailurePNStatus(new ReceiveError() { Status = 400 }); - PrepareAndEmitReceiveReconnectGiveupEvent(null); - } - } - - private void ReceiveReconnectTimerCallback(object state) - { - LogCallback?.Invoke("ReceiveReconnectingEffectHandler Timer interval invoke"); - ReceiveReconnectRequestEventArgs args = new ReceiveReconnectRequestEventArgs(); - args.ExtendedState = extendedState; - args.ReceiveReconnectResponseCallback = OnReceiveReconnectEffectResponseReceived; - OnReceiveReconnectRequested(args); - } - - public void OnReceiveReconnectEffectResponseReceived(string json) - { - try - { - pnStatus = null; - LogCallback?.Invoke($"OnReceiveReconnectEffectResponseReceived Json Response = {json}"); - var receivedResponse = JsonConvert.DeserializeObject>(json); - if (receivedResponse != null && receivedResponse.Timetoken != null) - { - receiveMessages = receivedResponse.Messages; - - ReceiveReconnectSuccess receiveReconnectSuccessEvent = new ReceiveReconnectSuccess(); - receiveReconnectSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); - receiveReconnectSuccessEvent.SubscriptionCursor.Timetoken = receivedResponse.Timetoken.Timestamp; - receiveReconnectSuccessEvent.SubscriptionCursor.Region = receivedResponse.Timetoken.Region; - receiveReconnectSuccessEvent.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; - receiveReconnectSuccessEvent.EventPayload.Region = receivedResponse.Timetoken.Region; - receiveReconnectSuccessEvent.EventType = EventType.ReceiveReconnectSuccess; - receiveReconnectSuccessEvent.Name = "RECEIVE_RECONNECT_SUCCESS"; - LogCallback?.Invoke("OnReceiveReconnectEffectResponseReceived - EventType.ReceiveReconnectSuccess"); - - pnStatus = new PNStatus(); - pnStatus.StatusCode = 200; - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNConnectedCategory; - pnStatus.Error = false; - - extendedState.Attempts = 0; - - eventEmitter.emit(receiveReconnectSuccessEvent); - } - else - { - ReceiveReconnectFailure receiveReconnectFailureEvent = new ReceiveReconnectFailure(); - receiveReconnectFailureEvent.Name = "RECEIVE_RECONNECT_FAILURE"; - receiveReconnectFailureEvent.EventType = EventType.ReceiveReconnectFailure; - LogCallback?.Invoke("OnReceivingReconnectEffectResponseReceived - EventType.ReceiveReconnectFailure"); - - pnStatus = new PNStatus(); - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Error = true; - - - var receiveReconnectError = JsonConvert.DeserializeObject(json); - extendedState.Attempts++; - PrepareFailurePNStatus(receiveReconnectError); - - if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnReceivingReconnectEffectResponseReceived - EventType.ReceiveReconnectGiveUp"); - PrepareAndEmitReceiveReconnectGiveupEvent(null); - } - else - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnReceivingReconnectEffectResponseReceived - EventType.ReceiveReconnectFailure"); - PrepareAndEmitReceiveReconnectFailureEvent(null); - } - } - } - catch (Exception ex) - { - extendedState.Attempts++; - PrepareFailurePNStatus(new ReceiveError() { Status = 400 }); - - if (MaxRetries != -1 && extendedState.Attempts > MaxRetries) - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.ReceiveReconnectGiveUp"); - PrepareAndEmitReceiveReconnectGiveupEvent(null); - - } - else - { - LogCallback?.Invoke($"Attempt: {extendedState.Attempts}; OnHandshakeReconnectEffectResponseReceived - EventType.ReceiveReconnectFailure"); - PrepareAndEmitReceiveReconnectFailureEvent(ex); - } - } - finally - { - if (timer != null) - { - try - { - timer.Change(Timeout.Infinite, Timeout.Infinite); - } - catch { } - } - } - } - - private void PrepareFailurePNStatus(ReceiveError error) - { - pnStatus = new PNStatus(); - pnStatus.StatusCode = (error != null && error.Status != 0) ? error.Status : 504; - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Category = PNStatusCategory.PNNetworkIssuesCategory; - pnStatus.Error = true; - } - - private void PrepareAndEmitReceiveReconnectFailureEvent(Exception ex) - { - ReceiveReconnectFailure receiveReconnectFailureEvent = new ReceiveReconnectFailure(); - receiveReconnectFailureEvent.Name = "RECEIVE_RECONNECT_FAILURE"; - receiveReconnectFailureEvent.EventType = EventType.ReceiveReconnectFailure; - receiveReconnectFailureEvent.Attempts = extendedState.Attempts; - if (ex != null) - { - receiveReconnectFailureEvent.EventPayload.exception = ex; - } - - eventEmitter.emit(receiveReconnectFailureEvent); - } - - private void PrepareAndEmitReceiveReconnectGiveupEvent(Exception ex) - { - ReceiveReconnectGiveUp receiveReconnectGiveupEvent = new ReceiveReconnectGiveUp(); - receiveReconnectGiveupEvent.Name = "RECEIVE_RECONNECT_GIVEUP"; - receiveReconnectGiveupEvent.EventType = EventType.ReceiveReconnectGiveUp; - receiveReconnectGiveupEvent.Attempts = extendedState.Attempts; - if (ex != null) - { - receiveReconnectGiveupEvent.EventPayload.exception = ex; - } - - eventEmitter.emit(receiveReconnectGiveupEvent); - } - - public void Cancel() - { - if (cancellationTokenSource != null) - { - LogCallback?.Invoke($"ReceiveReconnectEffectHandler - cancellationTokenSource - cancellion attempted."); - cancellationTokenSource.Cancel(); - } - if (timer != null) - { - timer.Change(Timeout.Infinite, Timeout.Infinite); - } - LogCallback?.Invoke($"ReceiveReconnectEffectHandler - invoking OnCancelReceiveReconnectRequested."); - CancelReceiveReconnectRequestEventArgs args = new CancelReceiveReconnectRequestEventArgs(); - OnCancelReceiveReconnectRequested(args); - } - public void Run(ExtendedState context) - { - if (AnnounceStatus != null && pnStatus != null) - { - AnnounceStatus(pnStatus); - } - } - public PNStatus GetPNStatus() - { - return pnStatus; - } - } -} diff --git a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs b/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs deleted file mode 100644 index 1c719582c..000000000 --- a/src/Api/PubnubApi/EventEngine/ReceivingEffectHandler.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace PubnubApi.PubnubEventEngine -{ - public class ReceiveingResponse - { - [JsonProperty("t")] - public Timetoken Timetoken { get; set; } - - [JsonProperty("m")] - public Message[] Messages { get; set; } - } - - public class Message - { - [JsonProperty ("a")] - public string Shard { get; set;} - - [JsonProperty ("b")] - public string SubscriptionMatch { get; set;} - - [JsonProperty("c")] - public string Channel { get; set; } - - [JsonProperty("d")] - public T Payload { get; set; } - - [JsonProperty("e")] - public int MessageType { get; set; } - - [JsonProperty("f")] - public string Flags { get; set; } - - //[JsonProperty("i")] - //public string IssuingClientId { get; set; } - - [JsonProperty("k")] - public string SubscribeKey { get; set; } - - [JsonProperty("o")] - public object OriginatingTimetoken { get; set; } - - [JsonProperty("p")] - public object PublishMetadata { get; set; } - - [JsonProperty("s")] - public long SequenceNumber { get; set; } - } - - public class PresenceEvent - { - [JsonProperty("action")] - public string Action { get; set; } - - [JsonProperty("uuid")] - public string Uuid { get; set; } - - [JsonProperty("timestamp")] - public long Timestamp { get; set; } - - [JsonProperty("occupancy")] - public int Occupancy { get; set; } - - } - - public class ReceiveError - { - [JsonProperty("status")] - public int Status { get; set; } - - [JsonProperty("error")] - public string ErrorMessage { get; set; } - } - - public class ReceiveRequestEventArgs : EventArgs - { - public ExtendedState ExtendedState { get; set; } - public Action ReceiveResponseCallback { get; set; } - } - - public class CancelReceiveRequestEventArgs : EventArgs - { - - } - - public class ReceivingEffectHandler : IEffectInvocationHandler, IReceiveMessageHandler - { - EventEmitter emitter; - private ExtendedState extendedState { get; set;} - private PNStatus pnStatus { get; set; } - private Message[] receiveMessages { get; set; } - public Action LogCallback { get; set; } - public Action AnnounceStatus { get; set; } - public Action> AnnounceMessage { get; set; } - public Action AnnouncePresenceEvent { get; set; } - public PNReconnectionPolicy ReconnectionPolicy { get; set; } - - public event EventHandler ReceiveRequested; - public event EventHandler CancelReceiveRequested; - protected virtual void OnReceiveRequested(ReceiveRequestEventArgs e) - { - EventHandler handler = ReceiveRequested; - if (handler != null) - { - handler(this, e); - } - } - - protected virtual void OnCancelReceiveRequested(CancelReceiveRequestEventArgs e) - { - EventHandler handler = CancelReceiveRequested; - if (handler != null) - { - handler(this, e); - } - } - - CancellationTokenSource cancellationTokenSource; - - public ReceivingEffectHandler(EventEmitter emitter) - { - this.emitter = emitter; - cancellationTokenSource = new CancellationTokenSource(); - } - - public async void Start(ExtendedState context, EventType eventType) - { - extendedState = context; - await Task.Factory.StartNew(() => { }); - if (cancellationTokenSource != null && cancellationTokenSource.Token.CanBeCanceled) { - Cancel(); - } - cancellationTokenSource = new CancellationTokenSource(); - - await Task.Factory.StartNew(() => { }); - ReceiveRequestEventArgs args = new ReceiveRequestEventArgs(); - args.ExtendedState = context; - args.ReceiveResponseCallback = OnReceivingEffectResponseReceived; - OnReceiveRequested(args); - } - - public void OnReceivingEffectResponseReceived(string json) - { - try - { - pnStatus = null; - var receivedResponse = JsonConvert.DeserializeObject>(json); - if (receivedResponse != null && receivedResponse.Timetoken != null) - { - receiveMessages = receivedResponse.Messages; - - ReceiveSuccess receiveSuccessEvent = new ReceiveSuccess(); - receiveSuccessEvent.SubscriptionCursor = new SubscriptionCursor(); - receiveSuccessEvent.SubscriptionCursor.Timetoken = receivedResponse.Timetoken.Timestamp; - receiveSuccessEvent.SubscriptionCursor.Region = receivedResponse.Timetoken.Region; - receiveSuccessEvent.EventPayload.Timetoken = receivedResponse.Timetoken.Timestamp; - receiveSuccessEvent.EventPayload.Region = receivedResponse.Timetoken.Region; - receiveSuccessEvent.EventType = EventType.ReceiveSuccess; - receiveSuccessEvent.Name = "RECEIVE_SUCCESS"; - LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveSuccess"); - - emitter.emit(receiveSuccessEvent); - } - else - { - ReceiveFailure receiveFailureEvent = new ReceiveFailure(); - receiveFailureEvent.Name = "RECEIVE_FAILURE"; - receiveFailureEvent.EventType = EventType.ReceiveFailure; - LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveFailure"); - - pnStatus = new PNStatus(); - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Error = true; - - emitter.emit(receiveFailureEvent); - } - } - catch (Exception ex) - { - LogCallback?.Invoke($"ReceivingEffectHandler EXCEPTION - {ex}"); - - ReceiveFailure receiveFailureEvent = new ReceiveFailure(); - receiveFailureEvent.Name = "RECEIVE_FAILURE"; - receiveFailureEvent.EventType = EventType.ReceiveFailure; - receiveFailureEvent.EventPayload.exception = ex; - LogCallback?.Invoke("OnReceivingEffectResponseReceived - EventType.ReceiveFailure"); - - pnStatus = new PNStatus(); - pnStatus.Operation = PNOperationType.PNSubscribeOperation; - pnStatus.AffectedChannels = extendedState.Channels; - pnStatus.AffectedChannelGroups = extendedState.ChannelGroups; - pnStatus.Error = true; - - emitter.emit(receiveFailureEvent); - } - } - - public void Cancel() - { - if (cancellationTokenSource != null) - { - LogCallback?.Invoke($"ReceivingEffectHandler - Receiving request cancellion attempted."); - cancellationTokenSource.Cancel(); - } - LogCallback?.Invoke($"ReceivingEffectHandler - invoking OnCancelReceiveRequested."); - CancelReceiveRequestEventArgs args = new CancelReceiveRequestEventArgs(); - OnCancelReceiveRequested(args); - } - public void Run(ExtendedState context) - { - if (AnnounceStatus != null && pnStatus != null) - { - AnnounceStatus(pnStatus); - } - Message[] receiveMessages = GetMessages(); - int messageCount = (receiveMessages != null) ? receiveMessages.Length : 0; - if (messageCount > 0) - { - for (int index = 0; index < receiveMessages.Length; index++) - { - LogCallback?.Invoke($"Received Message ({index + 1} of {receiveMessages.Length}) : {JsonConvert.SerializeObject(receiveMessages[index])}"); - if (receiveMessages[index].Channel.IndexOf("-pnpres") > 0) - { - if (AnnouncePresenceEvent != null) - { - var presenceEvent = JsonConvert.DeserializeObject(receiveMessages[index].Payload.ToString()); - PNPresenceEventResult presenceEventResult = new PNPresenceEventResult(); - presenceEventResult.Channel = receiveMessages[index].Channel; - presenceEventResult.Event = presenceEvent.Action; - presenceEventResult.Occupancy = presenceEvent.Occupancy; - presenceEventResult.Uuid = presenceEvent.Uuid; - presenceEventResult.Timestamp = presenceEvent.Timestamp; - presenceEventResult.UserMetadata = receiveMessages[index].PublishMetadata; - - AnnouncePresenceEvent?.Invoke(presenceEventResult); - } - } - else - { - if (receiveMessages[index].MessageType == 1) - { - //TODO: Callback for Signal message - PNSignalResult signalMessage = new PNSignalResult - { - Channel = receiveMessages[index].Channel, - Message = receiveMessages[index].Payload, - }; - AnnounceMessage?.Invoke(signalMessage); - } - else if (receiveMessages[index].MessageType == 2) - { - //TODO: Callback for Object message - } - else if (receiveMessages[index].MessageType == 3) - { - //TODO: Callback for Message Action message - } - else if (receiveMessages[index].MessageType == 4) - { - //TODO: Callback for File message - } - else - { - //Callback for regular message - if (AnnounceMessage != null) - { - LogCallback?.Invoke($"Message : {JsonConvert.SerializeObject(receiveMessages[index].Payload)}"); - PNMessageResult messageResult = new PNMessageResult(); - messageResult.Channel = receiveMessages[index].Channel; - messageResult.Message = receiveMessages[index].Payload; - AnnounceMessage?.Invoke(messageResult); - } - } - } - } - } - } - - public PNStatus GetPNStatus() - { - return pnStatus; - } - - public Message[] GetMessages() - { - return receiveMessages; - } - } -} diff --git a/src/Api/PubnubApi/EventEngine/State.cs b/src/Api/PubnubApi/EventEngine/State.cs deleted file mode 100644 index 9bbbe1e26..000000000 --- a/src/Api/PubnubApi/EventEngine/State.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PubnubApi.PubnubEventEngine -{ - - public enum StateType { Unsubscribed, Handshaking, HandshakingFailed, Receiving, ReceiveReconnecting, ReceiveStopped, ReceiveFailed, HandshakeFailed, HandshakeReconnecting, HandshakeStopped }; - - public class State - { - public EventType EventType { get; set; } - public StateType StateType { get; set; } - - public Dictionary transitions; - public Dictionary> EffectInvocationsList { get; private set; } - public List EntryList { get; private set; } - - public List ExitList { get; private set; } - - public State(StateType type) - { - this.StateType = type; - this.transitions = new Dictionary(); - //EffectInvocationsList = new List(); - EffectInvocationsList = new Dictionary>(); - } - - public State On(EventType e, StateType nextState) - { - transitions.Add(e, nextState); - return this; - } - public State On(EventType e, StateType nextState, List effectInvocation) - { - transitions.Add(e, nextState); - EffectInvocationsList.Add(e, effectInvocation); - return this; - } - - public State OnEntry(List entryInvocationList) - { - this.EntryList = entryInvocationList; - return this; - } - - public State OnExit(List exitInvocationList) - { - this.ExitList = exitInvocationList; - return this; - } - - //public State EffectInvocation(EffectInvocationType trigger, IEffectInvocationHandler effectInvocationHandler) - //{ - // this.EffectInvocationsList.Add(new EffectInvocation() { Effectype=trigger, Handler = effectInvocationHandler}); - // return this; - //} - } -} diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs index c3826ca42..2b27ff3e4 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; using System.Collections.Generic; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; namespace PubnubApi.EventEngine.Subscribe.Common { @@ -81,14 +81,15 @@ public class Message [JsonProperty("s")] public long SequenceNumber { get; set; } - [JsonProperty("p")] - public Timetoken Timetoken { get; set; } + [JsonProperty("u")] + public object UserMetadata { get; set; } } public abstract class SubscriptionState : Core.State { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; public ReconnectionConfiguration ReconnectionConfiguration; } } \ 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 e946b5c4a..dda186af9 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/EmitMessagesHandler.cs @@ -1,8 +1,12 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using PubnubApi.EventEngine.Core; +using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.Effects @@ -11,28 +15,77 @@ public class EmitMessagesHandler : EffectHandler> messageEmitterFunction; private readonly Pubnub pubnubInstance; - + private readonly Dictionary channelTypeMap; + private readonly Dictionary channelGroupTypeMap; + private readonly JsonSerializer serializer; + public EmitMessagesHandler(Pubnub pubnubInstance, - System.Action> messageEmitterFunction) + System.Action> messageEmitterFunction, + JsonSerializer serializer, + Dictionary channelTypeMap = null, + Dictionary channelGroupTypeMap = null) { this.messageEmitterFunction = messageEmitterFunction; this.pubnubInstance = pubnubInstance; + + this.channelTypeMap = channelTypeMap; + this.channelGroupTypeMap = channelGroupTypeMap; + + this.serializer = serializer; } public async override Task Run(EmitMessagesInvocation invocation) { - var processedMessages = invocation.Messages?.Messages.Select(m => new PNMessageResult() + var processedMessages = invocation.Messages?.Messages?.Select(m => { - Channel = m.Channel, - Message = JsonConvert.DeserializeObject(m.Payload), - Subscription = m.SubscriptionMatch, - Timetoken = m.Timetoken.Timestamp, - UserMetadata = m.PublishMetadata, - Publisher = m.IssuingClientId + var msgResult = new PNMessageResult() + { + Channel = m.Channel, + Subscription = m.SubscriptionMatch, + Timetoken = invocation.Cursor.Timetoken.Value, + UserMetadata = (m.UserMetadata as object), + // TODO Where do we put the publish timetoken metadata? + // UserMetadata = (m.PublishMetadata as JObject)?.ToObject(), + Publisher = m.IssuingClientId, + }; + + try + { + DeserializeMessage(channelTypeMap, m.Channel, msgResult, m.Payload); + DeserializeMessage(channelGroupTypeMap, m.Channel, msgResult, m.Payload); + } + catch (Exception e) + { + // TODO pass the exception + throw e; + } + + return msgResult; }); - processedMessages?.ToList().ForEach(message => messageEmitterFunction(pubnubInstance, message)); + if (processedMessages is null) return; + foreach (var message in processedMessages) + { + messageEmitterFunction(pubnubInstance, message); + } + } + + private void DeserializeMessage(Dictionary dict, string key, PNMessageResult msg, + object rawMessage) + { + if (dict is null) return; + if (rawMessage is JObject message) + { + Type t; + msg.Message = dict.TryGetValue(key, out t) && t != typeof(string) + ? message.ToObject(t, serializer) + : message.ToString(Formatting.None); + } + else + { + msg.Message = rawMessage; + } } 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 a517a0601..0cc601f28 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -7,20 +7,19 @@ using PubnubApi.EndPoint; using PubnubApi.EventEngine.Common; using PubnubApi.EventEngine.Core; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +using System.Diagnostics; namespace PubnubApi.EventEngine.Subscribe.Effects { public class HandshakeEffectHandler : - EffectDoubleCancellableHandler + EffectCancellableHandler { - private SubscribeManager2 manager; - private EventQueue eventQueue; - - private Delay retryDelay = new Delay(0); + private readonly SubscribeManager2 manager; + private readonly EventQueue eventQueue; internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { @@ -28,49 +27,43 @@ internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue this.eventQueue = eventQueue; } - public override async Task Run(HandshakeReconnectInvocation invocation) - { - if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) - { - eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); - } - else - { - retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); - await retryDelay.Start(); - if (!retryDelay.Cancelled) - await Run((HandshakeInvocation)invocation); - } - } - - public override bool IsBackground(HandshakeReconnectInvocation invocation) - { - return true; - } - public override async Task Run(HandshakeInvocation invocation) { var response = await MakeHandshakeRequest(invocation); SubscriptionCursor cursor = null; if (response.Item1 != null) { - cursor = new SubscriptionCursor() + if (invocation.Cursor != null && invocation.Cursor.Timetoken != null) + { + cursor = invocation.Cursor; + } + else if (response.Item1.Timetoken != null) { - Region = response.Item1.Timetoken.Region, - Timetoken = response.Item1.Timetoken.Timestamp - }; + cursor = new SubscriptionCursor() + { + Region = response.Item1.Timetoken.Region, + Timetoken = response.Item1.Timetoken.Timestamp + }; + } + } + else + { + if (invocation.Cursor != null && invocation.Cursor.Timetoken != null) + { + cursor = invocation.Cursor; + } } switch (invocation) { case Invocations.HandshakeReconnectInvocation reconnectInvocation when response.Item2.Error: - eventQueue.Enqueue(new Events.HandshakeReconnectFailureEvent() { AttemptedRetries = reconnectInvocation.AttemptedRetries + 1, Status = response.Item2}); + eventQueue.Enqueue(new Events.HandshakeReconnectFailureEvent() { AttemptedRetries = (reconnectInvocation.AttemptedRetries + 1) % int.MaxValue, Status = response.Item2}); break; case Invocations.HandshakeReconnectInvocation reconnectInvocation: eventQueue.Enqueue(new Events.HandshakeReconnectSuccessEvent() { Cursor = cursor, Status = response.Item2 }); break; case { } when response.Item2.Error: - eventQueue.Enqueue(new Events.HandshakeFailureEvent() { Status = response.Item2}); + eventQueue.Enqueue(new Events.HandshakeFailureEvent() { Status = response.Item2, Cursor = cursor, Channels = invocation.Channels, ChannelGroups = invocation.ChannelGroups}); break; case { }: eventQueue.Enqueue(new Events.HandshakeSuccessEvent() { Cursor = cursor, Status = response.Item2 }); @@ -100,15 +93,54 @@ public override bool IsBackground(HandshakeInvocation invocation) public override async Task Cancel() { - if (!retryDelay.Cancelled) + manager.HandshakeRequestCancellation(); + } + + } + + public class HandshakeReconnectEffectHandler : EffectCancellableHandler + { + private readonly EventQueue eventQueue; + + private HandshakeEffectHandler handshakeEffectHandler; + + private Delay retryDelay = new Delay(0); + + + internal HandshakeReconnectEffectHandler(SubscribeManager2 manager, EventQueue eventQueue, HandshakeEffectHandler handshakeEffectHandler) + { + this.eventQueue = eventQueue; + this.handshakeEffectHandler = handshakeEffectHandler; + } + + public override async Task Run(HandshakeReconnectInvocation invocation) + { + try { - retryDelay.Cancel(); + if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) + { + eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(new Exception(""), PNOperationType.PNSubscribeOperation, PNStatusCategory.PNCancelledCategory, invocation.Channels, invocation.ChannelGroups ) }); + } + else + { + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); + await retryDelay.Start(); + if (!retryDelay.Cancelled) + await handshakeEffectHandler.Run(invocation as HandshakeInvocation); + } } - else + catch (Exception ex) { - manager.HandshakeRequestCancellation(); + Debug.WriteLine(ex); } } + public override bool IsBackground(HandshakeReconnectInvocation invocation) => true; + + public override async Task Cancel() + { + retryDelay.Cancel(); + await handshakeEffectHandler.Cancel(); + } } } \ 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 index b2aa6b3fe..c0d7169aa 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -7,7 +7,7 @@ using PubnubApi.EndPoint; using PubnubApi.EventEngine.Common; using PubnubApi.EventEngine.Core; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; @@ -15,12 +15,10 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { public class ReceivingEffectHandler: - EffectDoubleCancellableHandler + EffectCancellableHandler { private SubscribeManager2 manager; private EventQueue eventQueue; - - private Delay retryDelay = new Delay(0); internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { @@ -28,49 +26,32 @@ internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue this.eventQueue = eventQueue; } - public override Task Run(ReceiveReconnectInvocation invocation) - { - if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) - { - eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); - } - else - { - retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); - // Run in the background - retryDelay.Start().ContinueWith((_) => this.Run((ReceiveMessagesInvocation)invocation)); - } - - return Utils.EmptyTask; - } - - public override bool IsBackground(ReceiveReconnectInvocation invocation) - { - return true; - } - public override async Task Run(ReceiveMessagesInvocation invocation) { var response = await MakeReceiveMessagesRequest(invocation); var cursor = new SubscriptionCursor() { - Region = response.Item1?.Timetoken.Region, - Timetoken = response.Item1?.Timetoken.Timestamp + Region = response.Item1?.Timetoken?.Region, + Timetoken = response.Item1?.Timetoken?.Timestamp }; + // Assume that if status is null, the effect was cancelled. + if (response.Item2 is null) + return; + switch (invocation) { case Invocations.ReceiveReconnectInvocation reconnectInvocation when response.Item2.Error: - eventQueue.Enqueue(new Events.ReceiveReconnectFailureEvent() { AttemptedRetries = reconnectInvocation.AttemptedRetries + 1, Status = response.Item2}); + eventQueue.Enqueue(new Events.ReceiveReconnectFailureEvent() { AttemptedRetries = (reconnectInvocation.AttemptedRetries + 1) % int.MaxValue, Status = response.Item2}); break; case Invocations.ReceiveReconnectInvocation reconnectInvocation: - eventQueue.Enqueue(new Events.ReceiveReconnectSuccessEvent() { Cursor = cursor, Status = response.Item2 }); + eventQueue.Enqueue(new Events.ReceiveReconnectSuccessEvent() { Channels = invocation?.Channels, ChannelGroups = invocation?.ChannelGroups, Cursor = cursor, Status = response.Item2, Messages = response.Item1 }); break; case { } when response.Item2.Error: - eventQueue.Enqueue(new Events.ReceiveFailureEvent() { Cursor = cursor, Status = response.Item2}); + eventQueue.Enqueue(new Events.ReceiveFailureEvent() { Cursor = invocation.Cursor, Status = response.Item2}); break; case { }: - eventQueue.Enqueue(new Events.ReceiveSuccessEvent() { Cursor = cursor, Messages= response.Item1, Status = response.Item2 }); + eventQueue.Enqueue(new Events.ReceiveSuccessEvent() { Channels = invocation?.Channels, ChannelGroups = invocation?.ChannelGroups, Cursor = cursor, Messages= response.Item1, Status = response.Item2 }); break; } } @@ -80,9 +61,9 @@ public override bool IsBackground(ReceiveMessagesInvocation invocation) return true; } - private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) + private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) { - return await manager.ReceiveRequest>( + return await manager.ReceiveRequest>( PNOperationType.PNSubscribeOperation, invocation.Channels?.ToArray(), invocation.ChannelGroups?.ToArray(), @@ -95,14 +76,50 @@ public override bool IsBackground(ReceiveMessagesInvocation invocation) public override async Task Cancel() { - if (!retryDelay.Cancelled) + manager.ReceiveRequestCancellation(); + } + } + + public class ReceivingReconnectEffectHandler : + EffectCancellableHandler + { + private SubscribeManager2 manager; + private EventQueue eventQueue; + private ReceivingEffectHandler receivingEffectHandler; + + private Delay retryDelay = new Delay(0); + + internal ReceivingReconnectEffectHandler(SubscribeManager2 manager, EventQueue eventQueue, ReceivingEffectHandler receivingEffectHandler) + { + this.manager = manager; + this.eventQueue = eventQueue; + this.receivingEffectHandler = receivingEffectHandler; + } + + public override async Task Run(ReceiveReconnectInvocation invocation) + { + if (!ReconnectionDelayUtil.shouldRetry(invocation.ReconnectionConfiguration, invocation.AttemptedRetries)) { - retryDelay.Cancel(); + eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); } else { - manager.ReceiveRequestCancellation(); + retryDelay = new Delay(ReconnectionDelayUtil.CalculateDelay(invocation.ReconnectionConfiguration.ReconnectionPolicy, invocation.AttemptedRetries)); + // Run in the background + await retryDelay.Start(); + await receivingEffectHandler.Run(invocation); } } + + public override bool IsBackground(ReceiveReconnectInvocation invocation) + { + return true; + } + + public override async Task Cancel() + { + retryDelay.Cancel(); + await receivingEffectHandler.Cancel(); + } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index c8334f8de..a9776544a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -3,69 +3,78 @@ namespace PubnubApi.EventEngine.Subscribe.Events { public class UnsubscribeAllEvent : Core.IEvent { + public string Name { get; set; } = "UNSUBSCRIBE_ALL"; } public class SubscriptionChangedEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; + public string Name { get; set; } = "SUBSCRIPTION_CHANGED"; } public class SubscriptionRestoredEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public string Name { get; set; } = "SUBSCRIPTION_RESTORED"; } public class HandshakeSuccessEvent : Core.IEvent { public SubscriptionCursor Cursor; public PNStatus Status; + public virtual string Name { get; set; } = "HANDSHAKE_SUCCESS"; } public class HandshakeFailureEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; public PNStatus Status; public int AttemptedRetries; + public virtual string Name { get; set; } = "HANDSHAKE_FAILURE"; } public class HandshakeReconnectSuccessEvent : HandshakeSuccessEvent { public PNStatus Status; public SubscriptionCursor Cursor; + public override string Name { get; set; } = "HANDSHAKE_RECONNECT_SUCCESS"; } public class HandshakeReconnectFailureEvent : HandshakeFailureEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; - } - - // Do we have this in system description ? - public class HandshakeReconnectRetryEvent : Core.IEvent { + public override string Name { get; set; } = "HANDSHAKE_RECONNECT_FAILURE"; } public class HandshakeReconnectGiveUpEvent : Core.IEvent { public PNStatus Status; + public string Name { get; set; } = "HANDSHAKE_RECONNECT_GIVEUP"; } public class ReceiveSuccessEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; - public ReceivingResponse Messages; + public ReceivingResponse Messages; public SubscriptionCursor Cursor; public PNStatus Status; + public virtual string Name { get; set; } = "RECEIVE_SUCCESS"; } public class ReceiveFailureEvent : Core.IEvent { public PNStatus Status; public int AttemptedRetries; public SubscriptionCursor Cursor; - } - - public class ReceiveReconnectRetry : Core.IEvent { + public virtual string Name { get; set; } = "RECEIVE_FAILURE"; } public class ReceiveReconnectSuccessEvent : ReceiveSuccessEvent { + public override string Name { get; set; } = "RECEIVE_RECONNECT_SUCCESS"; } public class ReceiveReconnectFailureEvent : ReceiveFailureEvent { + public override string Name { get; set; } = "RECEIVE_RECONNECT_FAILURE"; } public class ReceiveReconnectGiveUpEvent : Core.IEvent { @@ -73,17 +82,20 @@ public class ReceiveReconnectGiveUpEvent : Core.IEvent { public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; public PNStatus Status; + public string Name { get; set; } = "RECEIVE_RECONNECT_GIVEUP"; } public class DisconnectEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public string Name { get; set; } = "DISCONNECT"; } public class ReconnectEvent : Core.IEvent { public IEnumerable Channels; public IEnumerable ChannelGroups; public SubscriptionCursor Cursor; + public string Name { get; set; } = "RECONNECT"; } } \ 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 c4e4773bc..801eba604 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -1,14 +1,16 @@ using System.Collections.Generic; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; namespace PubnubApi.EventEngine.Subscribe.Invocations { public class EmitMessagesInvocation : Core.IEffectInvocation { - public ReceivingResponse Messages; - - public EmitMessagesInvocation(ReceivingResponse messages) + public ReceivingResponse Messages; + public SubscriptionCursor Cursor; + public string Name { get; set; } = "EMIT_MESSAGES"; + public EmitMessagesInvocation(SubscriptionCursor cursor, ReceivingResponse messages) { + this.Cursor = cursor; this.Messages = messages; } } @@ -17,6 +19,7 @@ public class EmitStatusInvocation : Core.IEffectInvocation { // TODO merge status variables into one? public PNStatusCategory StatusCategory; public PNStatus Status; + public string Name { get; set; } = "EMIT_STATUS"; public EmitStatusInvocation(PNStatus status) { @@ -40,9 +43,11 @@ public EmitStatusInvocation(PNStatusCategory category) public class HandshakeInvocation : Core.IEffectInvocation { public IEnumerable Channels; public IEnumerable ChannelGroups; + public SubscriptionCursor Cursor; // TODO if we need these, figure out how to pass them. public Dictionary InitialSubscribeQueryParams = new Dictionary(); public Dictionary ExternalQueryParams = new Dictionary(); + public virtual string Name { get; set; } = "HANDSHAKE"; } public class ReceiveMessagesInvocation : Core.IEffectInvocation @@ -52,25 +57,40 @@ public class ReceiveMessagesInvocation : Core.IEffectInvocation public SubscriptionCursor Cursor; public Dictionary InitialSubscribeQueryParams = new Dictionary(); public Dictionary ExternalQueryParams = new Dictionary(); + public virtual string Name { get; set; } = "RECEIVE_MESSAGES"; } - public class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation { } + public class CancelReceiveMessagesInvocation : ReceiveMessagesInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_RECEIVE_MESSAGES"; + } - public class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation { } + public class CancelHandshakeInvocation : HandshakeInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_HANDSHAKE"; + } public class HandshakeReconnectInvocation: HandshakeInvocation { public ReconnectionConfiguration ReconnectionConfiguration; public int AttemptedRetries; + public override string Name { get; set; } = "HANDSHAKE_RECONNECT"; } - public class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation { } + public class CancelHandshakeReconnectInvocation: HandshakeReconnectInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_HANDSHAKE_RECONNECT"; + } public class ReceiveReconnectInvocation: ReceiveMessagesInvocation { public ReconnectionConfiguration ReconnectionConfiguration; public int AttemptedRetries; + public override string Name { get; set; } = "RECEIVE_RECONNECT"; } - public class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation { } + public class CancelReceiveReconnectInvocation: ReceiveReconnectInvocation, Core.IEffectCancelInvocation + { + public override string Name { get; set; } = "CANCEL_RECEIVE_RECONNECT"; + } } \ 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 f01083e28..fa0ef9614 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -20,8 +21,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), ReconnectionConfiguration = this.ReconnectionConfiguration }, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index eae980448..1be3fe93d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -15,6 +16,7 @@ public class HandshakeReconnectingState : SubscriptionState { Channels = this.Channels, ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration, AttemptedRetries = this.AttemptedRetries }.AsArray(); @@ -31,8 +33,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), ReconnectionConfiguration = this.ReconnectionConfiguration }, @@ -57,8 +59,8 @@ public override TransitionResult Transition(IEvent e) Channels = this.Channels, ChannelGroups = this.ChannelGroups, ReconnectionConfiguration = this.ReconnectionConfiguration, - AttemptedRetries = this.AttemptedRetries + 1 - }.With(new EmitStatusInvocation(handshakeReconnectFailure.Status)), + AttemptedRetries = (this.AttemptedRetries + 1) % int.MaxValue + }, Events.HandshakeReconnectSuccessEvent handshakeReconnectSuccess => new ReceivingState() { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index db3455832..38aa5db57 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -20,8 +21,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), ReconnectionConfiguration = this.ReconnectionConfiguration }, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 457f9316a..16c99a392 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -1,18 +1,17 @@ using System; using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States { public class HandshakingState : SubscriptionState { - public SubscriptionCursor Cursor { get; set; } public override IEnumerable OnEntry => new HandshakeInvocation() - { Channels = this.Channels, - ChannelGroups = this.ChannelGroups }.AsArray(); + { Channels = this.Channels, ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); public override IEnumerable OnExit { get; } = new CancelHandshakeInvocation().AsArray(); @@ -27,8 +26,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new States.HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), ReconnectionConfiguration = this.ReconnectionConfiguration }, @@ -42,16 +41,18 @@ public override TransitionResult Transition(IEvent e) Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeReconnectingState() { - Channels = this.Channels, - ChannelGroups = this.ChannelGroups, + Channels = handshakeFailure.Channels, + ChannelGroups = handshakeFailure.ChannelGroups, + Cursor = handshakeFailure.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration, - AttemptedRetries = 0 - }.With(new EmitStatusInvocation(handshakeFailure.Status)), + AttemptedRetries = 1 + }, Events.DisconnectEvent disconnect => new States.HandshakeStoppedState() { Channels = disconnect.Channels, ChannelGroups = disconnect.ChannelGroups, + Cursor = disconnect.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs index 9fb34c77f..2a2f11579 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -3,14 +3,13 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; +using System.Linq; namespace PubnubApi.EventEngine.Subscribe.States { public class ReceiveFailedState : SubscriptionState { - public SubscriptionCursor Cursor; - public override IEnumerable OnEntry { get; } public override IEnumerable OnExit { get; } @@ -25,8 +24,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), Cursor = this.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 61dcbf397..9b5629cf1 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -3,17 +3,14 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; +using System.Linq; namespace PubnubApi.EventEngine.Subscribe.States { public class ReceiveReconnectingState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; - public ReconnectionConfiguration ReconnectionConfiguration; - public int AttemptedRetries; + public int AttemptedRetries { get; set;} public override IEnumerable OnEntry => new ReceiveReconnectInvocation() { @@ -38,8 +35,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), Cursor = this.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, @@ -66,7 +63,9 @@ public override TransitionResult Transition(IEvent e) ChannelGroups = receiveReconnectSuccess.ChannelGroups, Cursor = receiveReconnectSuccess.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration - }.With(new EmitStatusInvocation(receiveReconnectSuccess.Status)), + }.With( + new EmitMessagesInvocation(receiveReconnectSuccess.Cursor, receiveReconnectSuccess.Messages) + ), Events.ReceiveReconnectFailureEvent receiveReconnectFailure => new ReceiveReconnectingState() { @@ -74,7 +73,7 @@ public override TransitionResult Transition(IEvent e) ChannelGroups = this.ChannelGroups, Cursor = this.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration, - AttemptedRetries = this.AttemptedRetries + 1 + AttemptedRetries = (this.AttemptedRetries + 1) % int.MaxValue }.With(new EmitStatusInvocation(receiveReconnectFailure.Status)), Events.ReceiveReconnectGiveUpEvent receiveReconnectGiveUp => new ReceiveFailedState() diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs index 93c5236bd..49494ede2 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -3,14 +3,13 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; +using System.Linq; namespace PubnubApi.EventEngine.Subscribe.States { public class ReceiveStoppedState : SubscriptionState { - public SubscriptionCursor Cursor; - public override TransitionResult Transition(IEvent e) { return e switch @@ -22,8 +21,8 @@ public override TransitionResult Transition(IEvent e) Events.SubscriptionChangedEvent subscriptionChanged => new ReceiveStoppedState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), Cursor = this.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index 010541e09..277168866 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -3,14 +3,13 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; +using System.Linq; namespace PubnubApi.EventEngine.Subscribe.States { public class ReceivingState : SubscriptionState { - public SubscriptionCursor Cursor; - public override IEnumerable OnEntry => new ReceiveMessagesInvocation() { Channels = this.Channels,ChannelGroups = this.ChannelGroups, Cursor = this.Cursor }.AsArray(); @@ -32,15 +31,14 @@ public override TransitionResult Transition(IEvent e) Cursor = receiveSuccess.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }.With( - new EmitMessagesInvocation(receiveSuccess.Messages), - new EmitStatusInvocation(receiveSuccess.Status) + new EmitMessagesInvocation(receiveSuccess.Cursor, receiveSuccess.Messages) ), Events.SubscriptionChangedEvent subscriptionChanged => new ReceivingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, - Cursor = this.Cursor, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), + Cursor = subscriptionChanged.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, @@ -56,7 +54,7 @@ public override TransitionResult Transition(IEvent e) { Channels = this.Channels, ChannelGroups = this.ChannelGroups, - Cursor = this.Cursor, + Cursor = disconnect.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }.With(new EmitStatusInvocation(PNStatusCategory.PNDisconnectedCategory)), @@ -64,9 +62,9 @@ public override TransitionResult Transition(IEvent e) { Channels = this.Channels, ChannelGroups = this.ChannelGroups, - Cursor = this.Cursor, + Cursor = receiveFailure.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration, - AttemptedRetries = 0 + AttemptedRetries = 1 }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 132a0a06d..90e2072a6 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Invocations; namespace PubnubApi.EventEngine.Subscribe.States @@ -15,12 +16,13 @@ public override TransitionResult Transition(Core.IEvent e) { Events.SubscriptionChangedEvent subscriptionChanged => new HandshakingState() { - Channels = subscriptionChanged.Channels, - ChannelGroups = subscriptionChanged.ChannelGroups, + Channels = (Channels ?? Enumerable.Empty()).Union(subscriptionChanged.Channels), + ChannelGroups = (ChannelGroups ?? Enumerable.Empty()).Union(subscriptionChanged.ChannelGroups), + Cursor = subscriptionChanged.Cursor, ReconnectionConfiguration = this.ReconnectionConfiguration }, - Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() + Events.SubscriptionRestoredEvent subscriptionRestored => new States.HandshakingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, @@ -28,6 +30,13 @@ public override TransitionResult Transition(Core.IEvent e) ReconnectionConfiguration = this.ReconnectionConfiguration }, + Events.HandshakeFailureEvent handshakeFailure => new States.HandshakeReconnectingState() + { + Channels = handshakeFailure.Channels, + ChannelGroups = handshakeFailure.ChannelGroups, + Cursor = handshakeFailure.Cursor, + ReconnectionConfiguration = this.ReconnectionConfiguration + }, _ => null }; } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs index da88ecc14..b01eedcee 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -4,12 +4,24 @@ using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Effects; using PubnubApi.EventEngine.Subscribe.Invocations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; +using System.Collections.Generic; +using System.Linq; +using PubnubApi.EventEngine.Subscribe.Common; namespace PubnubApi.EventEngine.Subscribe { public class SubscribeEventEngine : Engine { private SubscribeManager2 subscribeManager; + private readonly Dictionary channelTypeMap = new Dictionary(); + private readonly Dictionary channelGroupTypeMap = new Dictionary(); + + private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings() + { Formatting = Formatting.None, DateParseHandling = DateParseHandling.None }; + private static readonly JsonSerializer Serializer = JsonSerializer.Create(SerializerSettings); + internal SubscribeEventEngine(Pubnub pubnubInstance, PNConfiguration pubnubConfiguration, SubscribeManager2 subscribeManager, @@ -20,18 +32,20 @@ internal SubscribeEventEngine(Pubnub pubnubInstance, // initialize the handler, pass dependencies var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, EventQueue); + var handshakeReconnectHandler = new Effects.HandshakeReconnectEffectHandler(subscribeManager, EventQueue, handshakeHandler); dispatcher.Register(handshakeHandler); dispatcher.Register(handshakeHandler); - dispatcher.Register(handshakeHandler); - dispatcher.Register(handshakeHandler); + dispatcher.Register(handshakeReconnectHandler); + dispatcher.Register(handshakeReconnectHandler); var receiveHandler = new Effects.ReceivingEffectHandler(subscribeManager, EventQueue); + var receiveReconnectHandler = new Effects.ReceivingReconnectEffectHandler(subscribeManager, EventQueue, receiveHandler); dispatcher.Register(receiveHandler); dispatcher.Register(receiveHandler); - dispatcher.Register(receiveHandler); - dispatcher.Register(receiveHandler); + dispatcher.Register(receiveReconnectHandler); + dispatcher.Register(receiveReconnectHandler); - var emitMessageHandler = new Effects.EmitMessagesHandler(pubnubInstance, messageListener); + var emitMessageHandler = new Effects.EmitMessagesHandler(pubnubInstance, messageListener, Serializer, channelTypeMap, channelGroupTypeMap); dispatcher.Register(emitMessageHandler); var emitStatusHandler = new Effects.EmitStatusEffectHandler(pubnubInstance, statusListener); @@ -39,13 +53,42 @@ internal SubscribeEventEngine(Pubnub pubnubInstance, currentState = new UnsubscribedState() { ReconnectionConfiguration = new Context.ReconnectionConfiguration(pubnubConfiguration.ReconnectionPolicy, pubnubConfiguration.ConnectionMaxRetries) }; } - public void Subscribe(string[] channels, string[] channelGroups) + public void Subscribe(string[] channels, string[] channelGroups, SubscriptionCursor cursor) + { + foreach (var c in channels) + { + channelTypeMap[c] = typeof(T); + } + foreach (var c in channelGroups) + { + channelGroupTypeMap[c] = typeof(T); + } + if (cursor != null) + { + this.EventQueue.Enqueue(new SubscriptionRestoredEvent() { Channels = channels, ChannelGroups = channelGroups, Cursor = cursor }); + } + else + { + this.EventQueue.Enqueue(new SubscriptionChangedEvent() { Channels = channels, ChannelGroups = channelGroups }); + } + } + + public void Subscribe(string[] channels, string[] channelGroups, SubscriptionCursor cursor) { - this.EventQueue.Enqueue(new SubscriptionChangedEvent() { Channels = channels, ChannelGroups = channelGroups }); + Subscribe(channels, channelGroups, cursor); } + public void UnsubscribeAll() { this.EventQueue.Enqueue(new UnsubscribeAllEvent()); } + + public void Unsubscribe(string[] channels, string[] channelGroups) + { + this.EventQueue.Enqueue(new SubscriptionChangedEvent() { + Channels = (this.currentState as SubscriptionState).Channels.Except(channels), + ChannelGroups = (this.currentState as SubscriptionState).ChannelGroups.Except(channelGroups) + }); + } } } \ No newline at end of file diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs index 723fbcb40..ae2a5ad20 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs @@ -6,31 +6,31 @@ namespace PubnubApi.EventEngine.Subscribe { public class SubscribeEventEngineFactory { - private ConcurrentDictionary engineinstances; + private ConcurrentDictionary engineInstances { get; set;} internal SubscribeEventEngineFactory() { - this.engineinstances = new ConcurrentDictionary(); + this.engineInstances = new ConcurrentDictionary(); } - internal bool hasEventEngine(string instanceId) + internal bool HasEventEngine(string instanceId) { - return engineinstances.ContainsKey(instanceId); + return engineInstances.ContainsKey(instanceId); } - internal SubscribeEventEngine getEventEngine(string instanceId) + internal SubscribeEventEngine GetEventEngine(string instanceId) { SubscribeEventEngine subscribeEventEngine; - engineinstances.TryGetValue(instanceId, out subscribeEventEngine); + engineInstances.TryGetValue(instanceId, out subscribeEventEngine); return subscribeEventEngine; } - internal SubscribeEventEngine initializeEventEngine(string instanceId, + internal SubscribeEventEngine InitializeEventEngine(string instanceId, Pubnub pubnubInstance, PNConfiguration pubnubConfiguration, SubscribeManager2 subscribeManager, Action statusListener = null, - Action> messageListener= null) + Action> messageListener= null) { - var subscribeEventEngine = new SubscribeEventEngine(pubnubInstance, pubnubConfiguration: pubnubConfiguration, subscribeManager,statusListener, null); //TODO: replace with message listener - if (engineinstances.TryAdd(instanceId, subscribeEventEngine)) { + var subscribeEventEngine = new SubscribeEventEngine(pubnubInstance, pubnubConfiguration: pubnubConfiguration, subscribeManager,statusListener, messageListener); + if (engineInstances.TryAdd(instanceId, subscribeEventEngine)) { return subscribeEventEngine; } else { diff --git a/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs b/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs index fd5de104e..ce0e7601c 100644 --- a/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs +++ b/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs @@ -1,5 +1,4 @@ -using PubnubApi.PubnubEventEngine; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; diff --git a/src/Api/PubnubApi/Interface/IUnsubscribeOperation.cs b/src/Api/PubnubApi/Interface/IUnsubscribeOperation.cs new file mode 100644 index 000000000..5693a129e --- /dev/null +++ b/src/Api/PubnubApi/Interface/IUnsubscribeOperation.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PubnubApi.Interface +{ + public interface IUnsubscribeOperation + { + IUnsubscribeOperation Channels(string[] channels); + IUnsubscribeOperation ChannelGroups(string[] channelGroups); + IUnsubscribeOperation QueryParam(Dictionary customQueryParam); + void Execute(); + } +} diff --git a/src/Api/PubnubApi/Pubnub.cs b/src/Api/PubnubApi/Pubnub.cs index dbbbd2a27..f79dbb18a 100644 --- a/src/Api/PubnubApi/Pubnub.cs +++ b/src/Api/PubnubApi/Pubnub.cs @@ -5,6 +5,8 @@ using PubnubApi.EventEngine.Subscribe; using PubnubApi.EndPoint; using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.Interface; #if !NET35 && !NET40 using System.Collections.Concurrent; #endif @@ -46,7 +48,7 @@ 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.subscribeEventEngineFactory,InstanceId ,this); + EndPoint.SubscribeEndpoint subscribeOperation = new EndPoint.SubscribeEndpoint(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this.subscribeEventEngineFactory,InstanceId ,this); subscribeOperation.SubscribeListenerList = subscribeCallbackListenerList; //subscribeOperation.CurrentPubnubInstance(this); @@ -62,11 +64,19 @@ public ISubscribeOperation Subscribe() } } - public EndPoint.UnsubscribeOperation Unsubscribe() + public IUnsubscribeOperation Unsubscribe() { - EndPoint.UnsubscribeOperation unsubscribeOperation = new EndPoint.UnsubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, this); - unsubscribeOperation.CurrentPubnubInstance(this); - return unsubscribeOperation; + if (pubnubConfig[InstanceId].EnableEventEngine) + { + EndPoint.UnsubscribeEndpoint unsubscribeOperation = new EndPoint.UnsubscribeEndpoint(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, subscribeEventEngineFactory, this); + return unsubscribeOperation; + } + else + { + EndPoint.UnsubscribeOperation unsubscribeOperation = new EndPoint.UnsubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, this); + unsubscribeOperation.CurrentPubnubInstance(this); + return unsubscribeOperation; + } } public EndPoint.UnsubscribeAllOperation UnsubscribeAll() @@ -539,12 +549,23 @@ public void SetAuthToken(string token) public bool Reconnect() { bool ret = false; - if (savedSubscribeOperation is EndPoint.SubscribeOperation) + if (pubnubConfig[InstanceId].EnableEventEngine) { - EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; - if (subscibeOperationInstance != null) + if (subscribeEventEngineFactory.HasEventEngine(InstanceId)) { - ret = subscibeOperationInstance.Retry(true, false); + var subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(InstanceId); + subscribeEventEngine.EventQueue.Enqueue(new ReconnectEvent() { Channels = (subscribeEventEngine.CurrentState as SubscriptionState).Channels, ChannelGroups = (subscribeEventEngine.CurrentState as SubscriptionState).ChannelGroups, Cursor = (subscribeEventEngine.CurrentState as SubscriptionState).Cursor }); + } + } + else + { + if (savedSubscribeOperation is EndPoint.SubscribeOperation) + { + EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; + if (subscibeOperationInstance != null) + { + ret = subscibeOperationInstance.Retry(true, false); + } } } return ret; @@ -553,12 +574,23 @@ public bool Reconnect() public bool Reconnect(bool resetSubscribeTimetoken) { bool ret = false; - if (savedSubscribeOperation is EndPoint.SubscribeOperation) + if (pubnubConfig[InstanceId].EnableEventEngine) { - EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; - if (subscibeOperationInstance != null) + if (subscribeEventEngineFactory.HasEventEngine(InstanceId)) { - ret = subscibeOperationInstance.Retry(true, resetSubscribeTimetoken); + var subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(InstanceId); + subscribeEventEngine.EventQueue.Enqueue(new ReconnectEvent() { Channels = (subscribeEventEngine.CurrentState as SubscriptionState).Channels, ChannelGroups = (subscribeEventEngine.CurrentState as SubscriptionState).ChannelGroups, Cursor = resetSubscribeTimetoken ? null : (subscribeEventEngine.CurrentState as SubscriptionState).Cursor }); + } + } + else + { + if (savedSubscribeOperation is EndPoint.SubscribeOperation) + { + EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; + if (subscibeOperationInstance != null) + { + ret = subscibeOperationInstance.Retry(true, resetSubscribeTimetoken); + } } } return ret; @@ -567,12 +599,23 @@ public bool Reconnect(bool resetSubscribeTimetoken) public bool Disconnect() { bool ret = false; - if (savedSubscribeOperation is EndPoint.SubscribeOperation) + if (pubnubConfig[InstanceId].EnableEventEngine) { - EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; - if (subscibeOperationInstance != null) + if (subscribeEventEngineFactory.HasEventEngine(InstanceId)) { - ret = subscibeOperationInstance.Retry(false); + var subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(InstanceId); + subscribeEventEngine.EventQueue.Enqueue(new DisconnectEvent() { Channels = (subscribeEventEngine.CurrentState as SubscriptionState).Channels, ChannelGroups = (subscribeEventEngine.CurrentState as SubscriptionState).ChannelGroups }); + } + } + else + { + if (savedSubscribeOperation is EndPoint.SubscribeOperation) + { + EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; + if (subscibeOperationInstance != null) + { + ret = subscibeOperationInstance.Retry(false); + } } } return ret; diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index b1ebf5097..b307b3763 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -142,4 +142,8 @@ + + + + diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 55134d8f3..3b62b0059 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -146,6 +146,7 @@ + EndPoint\PubSub\SubscribeManager.cs @@ -153,10 +154,10 @@ EndPoint\PubSub\SubscribeOperation.cs - EndPoint\PubSub\UnsubscribeAllOperation.cs + EndPoint\PubSub\UnsubscribeOperation.cs @@ -214,24 +215,14 @@ Enum\ResponseType.cs + + - - - - - - - - - - - - @@ -272,6 +263,7 @@ Interface\IPubnubUnitTest.cs + Interface\IUrlRequestBuilder.cs @@ -926,11 +918,12 @@ + + - diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 5cb01b762..76124ec94 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -268,8 +268,8 @@ EndPoint\PubSub\SubscribeOperation.cs - - EndPoint\PubSub\SubscribeOperation2.cs + + EndPoint\PubSub\SubscribeEndpoint.cs EndPoint\PubSub\UnsubscribeAllOperation.cs @@ -277,6 +277,9 @@ EndPoint\PubSub\UnsubscribeOperation.cs + + EndPoint\PubSub\UnsubscribeEndpoint.cs + EndPoint\Push\AddPushChannelOperation.cs @@ -331,24 +334,14 @@ Enum\ResponseType.cs + + - - - - - - - - - - - - @@ -385,6 +378,9 @@ Interface\ISubscribeOperation.cs + + Interface\IUnsubscribeOperation.cs + Interface\IPubnubLog.cs @@ -735,11 +731,12 @@ + + - diff --git a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature index 386935a3b..2ff925f57 100644 --- a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature +++ b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature @@ -1,33 +1,70 @@ -@featureSet=eventEngine +@featureSet=eventEngine @beta Feature: Event Engine This is a description of the feature Background: Given the demo keyset with event engine enabled - @contract=simpleSubscribe @beta + @contract=simpleSubscribe Scenario: Successfully receive messages When I subscribe - When I publish a message Then I receive the message in my subscribe response And I observe the following: - | type | name | - | event | SUBSCRIPTION_CHANGED | - | invocation | HANDSHAKE | - | event | HANDSHAKE_SUCCESS | - | invocation | CANCEL_HANDSHAKE | - | invocation | EMIT_STATUS | - | invocation | RECEIVE_EVENTS | - | event | RECEIVE_SUCCESS | - | invocation | CANCEL_RECEIVE_EVENTS | - | invocation | EMIT_STATUS | - | invocation | EMIT_EVENTS | + | type | name | + | event | SUBSCRIPTION_CHANGED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_SUCCESS | + | invocation | CANCEL_HANDSHAKE | + | invocation | EMIT_STATUS | + | invocation | RECEIVE_MESSAGES | + | event | RECEIVE_SUCCESS | + | invocation | CANCEL_RECEIVE_MESSAGES | + | invocation | EMIT_MESSAGES | + | invocation | RECEIVE_MESSAGES | - @contract=subscribeHandshakeFailure @beta + @contract=restoringSubscribe + Scenario: Successfully restore subscribe + When I subscribe with timetoken 42 + Then I receive the message in my subscribe response + And I observe the following: + | type | name | + | event | SUBSCRIPTION_RESTORED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_SUCCESS | + | invocation | CANCEL_HANDSHAKE | + | invocation | EMIT_STATUS | + | invocation | RECEIVE_MESSAGES | + | event | RECEIVE_SUCCESS | + | invocation | CANCEL_RECEIVE_MESSAGES | + | invocation | EMIT_MESSAGES | + | invocation | RECEIVE_MESSAGES | + + @contract=restoringSubscribeWithFailures + Scenario: Successfully restore subscribe with failures + Given a linear reconnection policy with 3 retries + When I subscribe with timetoken 42 + Then I receive the message in my subscribe response + And I observe the following: + | type | name | + | event | SUBSCRIPTION_RESTORED | + | invocation | HANDSHAKE | + | event | HANDSHAKE_FAILURE | + | invocation | CANCEL_HANDSHAKE | + | invocation | HANDSHAKE_RECONNECT | + | event | HANDSHAKE_RECONNECT_SUCCESS | + | invocation | CANCEL_HANDSHAKE_RECONNECT | + | invocation | EMIT_STATUS | + | invocation | RECEIVE_MESSAGES | + | event | RECEIVE_SUCCESS | + | invocation | CANCEL_RECEIVE_MESSAGES | + | invocation | EMIT_MESSAGES | + | invocation | RECEIVE_MESSAGES | + + @contract=subscribeHandshakeFailure Scenario: Complete handshake failure Given a linear reconnection policy with 3 retries When I subscribe - Then I receive an error + Then I receive an error in my subscribe response And I observe the following: | type | name | | event | SUBSCRIPTION_CHANGED | @@ -48,7 +85,7 @@ Feature: Event Engine | invocation | CANCEL_HANDSHAKE_RECONNECT | | invocation | EMIT_STATUS | - @contract=subscribeHandshakeRecovery @beta + @contract=subscribeHandshakeRecovery Scenario: Handshake failure recovery Given a linear reconnection policy with 3 retries When I subscribe @@ -66,14 +103,15 @@ Feature: Event Engine | event | HANDSHAKE_RECONNECT_SUCCESS | | invocation | CANCEL_HANDSHAKE_RECONNECT | | invocation | EMIT_STATUS | - | invocation | RECEIVE_EVENTS | + | invocation | RECEIVE_MESSAGES | | event | RECEIVE_SUCCESS | - | invocation | CANCEL_RECEIVE_EVENTS | - | invocation | EMIT_STATUS | - | invocation | EMIT_EVENTS | + | invocation | CANCEL_RECEIVE_MESSAGES | + | invocation | EMIT_MESSAGES | + | invocation | RECEIVE_MESSAGES | - @contract=subscribeReceivingRecovery @beta + @contract=subscribeReceivingRecovery Scenario: Receiving failure recovery + Given a linear reconnection policy with 3 retries When I subscribe Then I receive the message in my subscribe response And I observe the following: @@ -83,12 +121,11 @@ Feature: Event Engine | event | HANDSHAKE_SUCCESS | | invocation | CANCEL_HANDSHAKE | | invocation | EMIT_STATUS | - | invocation | RECEIVE_EVENTS | + | invocation | RECEIVE_MESSAGES | | event | RECEIVE_FAILURE | - | invocation | CANCEL_RECEIVE_EVENTS | + | invocation | CANCEL_RECEIVE_MESSAGES | | invocation | RECEIVE_RECONNECT | | event | RECEIVE_RECONNECT_SUCCESS | | invocation | CANCEL_RECEIVE_RECONNECT | - | invocation | EMIT_STATUS | - | invocation | EMIT_EVENTS | - | invocation | RECEIVE_EVENTS | \ No newline at end of file + | invocation | EMIT_MESSAGES | + | invocation | RECEIVE_MESSAGES | \ No newline at end of file diff --git a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs index 841b19aba..c5f4e54a6 100644 --- a/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs +++ b/src/UnitTests/AcceptanceTests/Features/event-engine/happy-path.feature.cs @@ -22,13 +22,15 @@ namespace AcceptanceTests.Features.Event_Engine [NUnit.Framework.TestFixtureAttribute()] [NUnit.Framework.DescriptionAttribute("Event Engine")] [NUnit.Framework.CategoryAttribute("featureSet=eventEngine")] + [NUnit.Framework.CategoryAttribute("beta")] public partial class EventEngineFeature { private TechTalk.SpecFlow.ITestRunner testRunner; private static string[] featureTags = new string[] { - "featureSet=eventEngine"}; + "featureSet=eventEngine", + "beta"}; #line 1 "happy-path.feature" #line hidden @@ -87,12 +89,10 @@ public virtual void FeatureBackground() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Successfully receive messages")] [NUnit.Framework.CategoryAttribute("contract=simpleSubscribe")] - [NUnit.Framework.CategoryAttribute("beta")] public void SuccessfullyReceiveMessages() { string[] tagsOfScenario = new string[] { - "contract=simpleSubscribe", - "beta"}; + "contract=simpleSubscribe"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Successfully receive messages", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 9 @@ -112,9 +112,6 @@ public void SuccessfullyReceiveMessages() testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line hidden #line 11 - testRunner.When("I publish a message", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); -#line hidden -#line 12 testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { @@ -137,38 +134,184 @@ public void SuccessfullyReceiveMessages() "EMIT_STATUS"}); table1.AddRow(new string[] { "invocation", - "RECEIVE_EVENTS"}); + "RECEIVE_MESSAGES"}); table1.AddRow(new string[] { "event", "RECEIVE_SUCCESS"}); table1.AddRow(new string[] { "invocation", - "CANCEL_RECEIVE_EVENTS"}); + "CANCEL_RECEIVE_MESSAGES"}); table1.AddRow(new string[] { "invocation", - "EMIT_STATUS"}); + "EMIT_MESSAGES"}); table1.AddRow(new string[] { "invocation", - "EMIT_EVENTS"}); -#line 13 + "RECEIVE_MESSAGES"}); +#line 12 testRunner.And("I observe the following:", ((string)(null)), table1, "And "); #line hidden } this.ScenarioCleanup(); } + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Successfully restore subscribe")] + [NUnit.Framework.CategoryAttribute("contract=restoringSubscribe")] + public void SuccessfullyRestoreSubscribe() + { + string[] tagsOfScenario = new string[] { + "contract=restoringSubscribe"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Successfully restore subscribe", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 26 + this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 5 + this.FeatureBackground(); +#line hidden +#line 27 + testRunner.When("I subscribe with timetoken 42", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 28 + testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { + "type", + "name"}); + table2.AddRow(new string[] { + "event", + "SUBSCRIPTION_RESTORED"}); + table2.AddRow(new string[] { + "invocation", + "HANDSHAKE"}); + table2.AddRow(new string[] { + "event", + "HANDSHAKE_SUCCESS"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE"}); + table2.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table2.AddRow(new string[] { + "invocation", + "RECEIVE_MESSAGES"}); + table2.AddRow(new string[] { + "event", + "RECEIVE_SUCCESS"}); + table2.AddRow(new string[] { + "invocation", + "CANCEL_RECEIVE_MESSAGES"}); + table2.AddRow(new string[] { + "invocation", + "EMIT_MESSAGES"}); + table2.AddRow(new string[] { + "invocation", + "RECEIVE_MESSAGES"}); +#line 29 + testRunner.And("I observe the following:", ((string)(null)), table2, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Successfully restore subscribe with failures")] + [NUnit.Framework.CategoryAttribute("contract=restoringSubscribeWithFailures")] + public void SuccessfullyRestoreSubscribeWithFailures() + { + string[] tagsOfScenario = new string[] { + "contract=restoringSubscribeWithFailures"}; + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Successfully restore subscribe with failures", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 43 + this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 5 + this.FeatureBackground(); +#line hidden +#line 44 + testRunner.Given("a linear reconnection policy with 3 retries", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 45 + testRunner.When("I subscribe with timetoken 42", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 46 + testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { + "type", + "name"}); + table3.AddRow(new string[] { + "event", + "SUBSCRIPTION_RESTORED"}); + table3.AddRow(new string[] { + "invocation", + "HANDSHAKE"}); + table3.AddRow(new string[] { + "event", + "HANDSHAKE_FAILURE"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE"}); + table3.AddRow(new string[] { + "invocation", + "HANDSHAKE_RECONNECT"}); + table3.AddRow(new string[] { + "event", + "HANDSHAKE_RECONNECT_SUCCESS"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_HANDSHAKE_RECONNECT"}); + table3.AddRow(new string[] { + "invocation", + "EMIT_STATUS"}); + table3.AddRow(new string[] { + "invocation", + "RECEIVE_MESSAGES"}); + table3.AddRow(new string[] { + "event", + "RECEIVE_SUCCESS"}); + table3.AddRow(new string[] { + "invocation", + "CANCEL_RECEIVE_MESSAGES"}); + table3.AddRow(new string[] { + "invocation", + "EMIT_MESSAGES"}); + table3.AddRow(new string[] { + "invocation", + "RECEIVE_MESSAGES"}); +#line 47 + testRunner.And("I observe the following:", ((string)(null)), table3, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Complete handshake failure")] [NUnit.Framework.CategoryAttribute("contract=subscribeHandshakeFailure")] - [NUnit.Framework.CategoryAttribute("beta")] public void CompleteHandshakeFailure() { string[] tagsOfScenario = new string[] { - "contract=subscribeHandshakeFailure", - "beta"}; + "contract=subscribeHandshakeFailure"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Complete handshake failure", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 27 +#line 64 this.ScenarioInitialize(scenarioInfo); #line hidden if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) @@ -181,71 +324,71 @@ public void CompleteHandshakeFailure() #line 5 this.FeatureBackground(); #line hidden -#line 28 +#line 65 testRunner.Given("a linear reconnection policy with 3 retries", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line hidden -#line 29 +#line 66 testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line hidden -#line 30 - testRunner.Then("I receive an error", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line 67 + testRunner.Then("I receive an error in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden - TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { + TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { "type", "name"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "event", "SUBSCRIPTION_CHANGED"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "HANDSHAKE"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "event", "HANDSHAKE_FAILURE"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "event", "HANDSHAKE_RECONNECT_FAILURE"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "event", "HANDSHAKE_RECONNECT_FAILURE"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "event", "HANDSHAKE_RECONNECT_FAILURE"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "event", "HANDSHAKE_RECONNECT_GIVEUP"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE_RECONNECT"}); - table2.AddRow(new string[] { + table4.AddRow(new string[] { "invocation", "EMIT_STATUS"}); -#line 31 - testRunner.And("I observe the following:", ((string)(null)), table2, "And "); +#line 68 + testRunner.And("I observe the following:", ((string)(null)), table4, "And "); #line hidden } this.ScenarioCleanup(); @@ -254,15 +397,13 @@ public void CompleteHandshakeFailure() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Handshake failure recovery")] [NUnit.Framework.CategoryAttribute("contract=subscribeHandshakeRecovery")] - [NUnit.Framework.CategoryAttribute("beta")] public void HandshakeFailureRecovery() { string[] tagsOfScenario = new string[] { - "contract=subscribeHandshakeRecovery", - "beta"}; + "contract=subscribeHandshakeRecovery"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Handshake failure recovery", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 52 +#line 89 this.ScenarioInitialize(scenarioInfo); #line hidden if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) @@ -275,68 +416,68 @@ public void HandshakeFailureRecovery() #line 5 this.FeatureBackground(); #line hidden -#line 53 +#line 90 testRunner.Given("a linear reconnection policy with 3 retries", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line hidden -#line 54 +#line 91 testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line hidden -#line 55 +#line 92 testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden - TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { + TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { "type", "name"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "event", "SUBSCRIPTION_CHANGED"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "HANDSHAKE"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "event", "HANDSHAKE_FAILURE"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "HANDSHAKE_RECONNECT"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "event", "HANDSHAKE_RECONNECT_FAILURE"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE_RECONNECT"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "HANDSHAKE_RECONNECT"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "event", "HANDSHAKE_RECONNECT_SUCCESS"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE_RECONNECT"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", "EMIT_STATUS"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", - "RECEIVE_EVENTS"}); - table3.AddRow(new string[] { + "RECEIVE_MESSAGES"}); + table5.AddRow(new string[] { "event", "RECEIVE_SUCCESS"}); - table3.AddRow(new string[] { + table5.AddRow(new string[] { "invocation", - "CANCEL_RECEIVE_EVENTS"}); - table3.AddRow(new string[] { + "CANCEL_RECEIVE_MESSAGES"}); + table5.AddRow(new string[] { "invocation", - "EMIT_STATUS"}); - table3.AddRow(new string[] { + "EMIT_MESSAGES"}); + table5.AddRow(new string[] { "invocation", - "EMIT_EVENTS"}); -#line 56 - testRunner.And("I observe the following:", ((string)(null)), table3, "And "); + "RECEIVE_MESSAGES"}); +#line 93 + testRunner.And("I observe the following:", ((string)(null)), table5, "And "); #line hidden } this.ScenarioCleanup(); @@ -345,15 +486,13 @@ public void HandshakeFailureRecovery() [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Receiving failure recovery")] [NUnit.Framework.CategoryAttribute("contract=subscribeReceivingRecovery")] - [NUnit.Framework.CategoryAttribute("beta")] public void ReceivingFailureRecovery() { string[] tagsOfScenario = new string[] { - "contract=subscribeReceivingRecovery", - "beta"}; + "contract=subscribeReceivingRecovery"}; System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Receiving failure recovery", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 76 +#line 113 this.ScenarioInitialize(scenarioInfo); #line hidden if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) @@ -366,59 +505,59 @@ public void ReceivingFailureRecovery() #line 5 this.FeatureBackground(); #line hidden -#line 77 +#line 114 + testRunner.Given("a linear reconnection policy with 3 retries", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 115 testRunner.When("I subscribe", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line hidden -#line 78 +#line 116 testRunner.Then("I receive the message in my subscribe response", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden - TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { + TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { "type", "name"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "event", "SUBSCRIPTION_CHANGED"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", "HANDSHAKE"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "event", "HANDSHAKE_SUCCESS"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", "CANCEL_HANDSHAKE"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", "EMIT_STATUS"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", - "RECEIVE_EVENTS"}); - table4.AddRow(new string[] { + "RECEIVE_MESSAGES"}); + table6.AddRow(new string[] { "event", "RECEIVE_FAILURE"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", - "CANCEL_RECEIVE_EVENTS"}); - table4.AddRow(new string[] { + "CANCEL_RECEIVE_MESSAGES"}); + table6.AddRow(new string[] { "invocation", "RECEIVE_RECONNECT"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "event", "RECEIVE_RECONNECT_SUCCESS"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", "CANCEL_RECEIVE_RECONNECT"}); - table4.AddRow(new string[] { - "invocation", - "EMIT_STATUS"}); - table4.AddRow(new string[] { + table6.AddRow(new string[] { "invocation", - "EMIT_EVENTS"}); - table4.AddRow(new string[] { + "EMIT_MESSAGES"}); + table6.AddRow(new string[] { "invocation", - "RECEIVE_EVENTS"}); -#line 79 - testRunner.And("I observe the following:", ((string)(null)), table4, "And "); + "RECEIVE_MESSAGES"}); +#line 117 + testRunner.And("I observe the following:", ((string)(null)), table6, "And "); #line hidden } this.ScenarioCleanup(); diff --git a/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/grant-token.feature.cs index c8bb56301..a1aaaf549 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\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); + 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); testRunner.OnFeatureStart(featureInfo); } diff --git a/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs b/src/UnitTests/AcceptanceTests/Features/revoke-token.feature.cs index f98f833b4..d5a85c045 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\n specific PubN" + - "ub 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\r\n specific Pub" + + "Nub resources by revoking corresponding tokens.", ProgrammingLanguage.CSharp, featureTags); testRunner.OnFeatureStart(featureInfo); } diff --git a/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs index 9b2edae6b..0a6b235df 100644 --- a/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs +++ b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs @@ -11,16 +11,16 @@ using System.Text.Json; using System.Threading.Channels; using System.Threading; -using PubnubApi.PubnubEventEngine; using TechTalk.SpecFlow.Assist; using System.Net.Http; +using System.Diagnostics; namespace AcceptanceTests.Steps { [Binding] public class EventEngineSteps { - public static bool enableIntenalPubnubLogging = false; + public static bool enableIntenalPubnubLogging = true; public static string currentFeature = string.Empty; public static string currentContract = string.Empty; public static bool betaVersion = false; @@ -32,10 +32,6 @@ public class EventEngineSteps private string channel = "my_channel"; private string channelGroup = "my_channelgroup"; private string publishMsg = "hello_world"; - //private UuidMetadataPersona uuidMetadataPersona = null; - //private PNGetUuidMetadataResult getUuidMetadataResult = null; - //private PNSetUuidMetadataResult setUuidMetadataResult = null; - //private PNGetAllUuidMetadataResult getAllUuidMetadataResult = null; PNPublishResult publishResult = null; SubscribeCallback subscribeCallback = null; private PNMessageResult messageResult = null; @@ -45,6 +41,11 @@ public class EventEngineSteps PubnubError pnError = null; IPubnubUnitTest unitTest; + static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) + { + Debug.WriteLine("Unhandled exception occured inside EventEngine. Exiting the test. Please try again."); + System.Environment.Exit(1); + } public class PubnubUnitTest : IPubnubUnitTest { long IPubnubUnitTest.Timetoken @@ -136,6 +137,7 @@ public class SubscribeResponseRow } public EventEngineSteps(ScenarioContext scenarioContext) { + AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper; _scenarioContext = scenarioContext; } @@ -208,6 +210,7 @@ public void AfterScenario() System.Diagnostics.Debug.WriteLine(mockExpectResponse); } } + [Given(@"the demo keyset with event engine enabled")] public void GivenTheDemoKeysetWithEventEngineEnabled() { @@ -236,7 +239,6 @@ public void GivenTheDemoKeysetWithEventEngineEnabled() } config.EnableEventEngine = true; - messageReceivedEvent = new ManualResetEvent(false); statusReceivedEvent = new ManualResetEvent(false); @@ -313,6 +315,36 @@ public void WhenISubscribe() } } + [When(@"I subscribe with timetoken (.*)")] + public void WhenISubscribeWithTimetoken(int p0) + { + pn = new Pubnub(config); + pn.PubnubUnitTest = unitTest; + pn.PubnubUnitTest.EventTypeList?.Clear(); + + messageReceivedEvent = new ManualResetEvent(false); + statusReceivedEvent = new ManualResetEvent(false); + + pn.AddListener(subscribeCallback); + pn.Subscribe() + .Channels(channel.Split(',')) + .ChannelGroups(channelGroup.Split(',')) + .WithTimetoken(p0) + .Execute(); + statusReceivedEvent.WaitOne (60*1000); + if (pnStatus != null && pnStatus.Category == PNStatusCategory.PNConnectedCategory) + { + //All good. + } + else + { + if (currentContract == "simpleSubscribe") + { + Assert.Fail("WhenISubscribe failed."); + } + } + } + [When(@"I publish a message")] public async Task WhenIPublishAMessage() { @@ -339,8 +371,8 @@ public void ThenIObserveTheFollowing(Table table) { Assert.Fail(); } - System.Diagnostics.Debug.WriteLine($"COUNT = {pn.PubnubUnitTest.EventTypeList.Count} "); - for (int i = 0; i < pn.PubnubUnitTest.EventTypeList.Count(); i++) + System.Diagnostics.Debug.WriteLine($"COUNT = {pn.PubnubUnitTest.EventTypeList?.Count} "); + for (int i = 0; i < pn.PubnubUnitTest.EventTypeList?.Count(); i++) { System.Diagnostics.Debug.WriteLine($"{pn.PubnubUnitTest.EventTypeList[i].Key} - {pn.PubnubUnitTest.EventTypeList[i].Value} "); } @@ -372,11 +404,16 @@ public void GivenALinearReconnectionPolicyWithRetries(int retryCount) config.ConnectionMaxRetries = retryCount; } + [Then(@"I receive an error in my subscribe response")] + public void ThenIReceiveAnErrorInMySubscribeResponse() + { + Assert.True(pnStatus != null && pnStatus.Error); + } + [Then(@"I receive an error")] public void ThenIReceiveAnError() { Assert.True(pnStatus != null && pnStatus.Error); } - } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs index 9d214e61a..ac110ddf2 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakeReconnectingStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.States; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs index 55e75a9af..c9242b2b0 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/HandshakingStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.States; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs index e4f85e7a8..b64ba5db1 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveFailedStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.States; using System.Linq; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs index 3b265a847..26ce07fd7 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveReconnectingStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.States; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs index f560f8a14..11bc89c35 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceiveStoppedStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.States; using System.Linq; diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs index 50e3fafd5..16d759f04 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/ReceivingStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.States; @@ -41,7 +41,7 @@ internal class ReceivingStateTransition 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 } } + 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) } } diff --git a/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs b/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs index 6147eec88..c3cf81119 100644 --- a/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs +++ b/src/UnitTests/PubnubApi.Tests/EventEngine/UnsubscribedStateTransition.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; -using PubnubApi.EventEngine.Subscribe.Context; +using PubnubApi.EventEngine.Context; using PubnubApi.EventEngine.Subscribe.Events; using PubnubApi.EventEngine.Subscribe.States; using System.Linq;