diff --git a/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs b/src/Api/PubnubApi/Builder/UrlRequestBuilder.cs index 135ec60c7..e363c91a2 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", ""); + } + if (!requestQueryStringParams.ContainsKey("tt")) { requestQueryStringParams.Add("tt", timetoken.ToString(CultureInfo.InvariantCulture)); diff --git a/src/Api/PubnubApi/EndPoint/Presence/PresenceOperation.cs b/src/Api/PubnubApi/EndPoint/Presence/PresenceOperation.cs index bdea463b6..9186e245b 100644 --- a/src/Api/PubnubApi/EndPoint/Presence/PresenceOperation.cs +++ b/src/Api/PubnubApi/EndPoint/Presence/PresenceOperation.cs @@ -11,16 +11,16 @@ public class PresenceOperation public PresenceOperation(Pubnub instance, string instanceId, IPubnubLog pubnubLog, TelemetryManager telemetryManager, TokenManager tokenManager, PresenceEventEngineFactory presenceEventEngineFactory) { this.presenceEventEngineFactory = presenceEventEngineFactory; - if (this.presenceEventEngineFactory.hasEventEngine(instanceId)) { - presenceEventEngine = this.presenceEventEngineFactory.getEventEngine(instanceId); + if (this.presenceEventEngineFactory.HasEventEngine(instanceId)) { + presenceEventEngine = this.presenceEventEngineFactory.GetEventEngine(instanceId); } else { - presenceEventEngine = this.presenceEventEngineFactory.initializeEventEngine(instanceId, instance, pubnubLog, telemetryManager, tokenManager); + presenceEventEngine = this.presenceEventEngineFactory.InitializeEventEngine(instanceId, instance, pubnubLog, telemetryManager, tokenManager); } } public void Start(string[] channels, string[] channelGroups) { - this.presenceEventEngine.eventQueue.Enqueue(new JoinedEvent() { + this.presenceEventEngine.EventQueue.Enqueue(new JoinedEvent() { Input = new EventEngine.Presence.Common.PresenceInput() { Channels = channels, ChannelGroups = channelGroups } }); } diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs new file mode 100644 index 000000000..a11e99604 --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeEndpoint.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Globalization; +using PubnubApi.EventEngine.Subscribe; +using PubnubApi.EventEngine.Core; +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; + private PresenceOperation presenceOperation; + 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, PresenceOperation presenceOperation , string instanceId, Pubnub instance) + { + PubnubInstance = instance; + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + pubnubTokenMgr = tokenManager; + this.subscribeEventEngineFactory = subscribeEventEngineFactory; + this.presenceOperation = presenceOperation; + 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() + { + subscribeChannelNames ??= new List(); + subscribeChannelGroupNames ??= new List(); + + if (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 = subscribeChannelNames != null ? this.subscribeChannelNames.ToArray() : null; + string[] channelGroupNames = 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?.Length ?? 0) == 0 && (channelGroups?.Length ?? 0) == 0) { + throw new ArgumentException("Either Channel Or Channel Group or Both should be provided."); + } + + if (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); + if (this.presenceOperation != null) { + presenceOperation.Start(channels?.Where(c => !c.EndsWith("-pnpres")).ToArray(), channelGroups?.Where(cg => !cg.EndsWith("-pnpres")).ToArray()); + } + } + + 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 23fa5be6a..f77ba4fc7 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeManager2.cs @@ -7,6 +7,8 @@ using System.Globalization; 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; @@ -92,39 +94,31 @@ public SubscribeManager2(PNConfiguration pubnubConfig, IJsonPluggableLibrary jso #pragma warning disable - internal async Task> HandshakeRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + public async Task> HandshakeRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) { - Tuple resp = new Tuple ("", null); - - try - { string presenceState = string.Empty; if (config.MaintainPresenceState) presenceState = BuildJsonUserState(channels, channelGroups, true); - IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, null, ""); - Uri request = urlBuilder.BuildMultiChannelSubscribeRequest("GET", "", channels, channelGroups, timetoken.GetValueOrDefault(), region.GetValueOrDefault(), presenceState, initialSubscribeUrlParams, externalQueryParam); + IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, null, ""); + Uri request = urlBuilder.BuildMultiChannelSubscribeRequest("GET", "", channels, channelGroups, timetoken.GetValueOrDefault(), region.GetValueOrDefault(), presenceState, initialSubscribeUrlParams, externalQueryParam); - RequestState pubnubRequestState = new RequestState(); - pubnubRequestState.Channels = channels; - pubnubRequestState.ChannelGroups = channelGroups; - pubnubRequestState.ResponseType = responseType; - //pubnubRequestState.Reconnect = reconnect; - pubnubRequestState.Timetoken = timetoken.GetValueOrDefault(); - pubnubRequestState.Region = region.GetValueOrDefault(); - pubnubRequestState.TimeQueued = DateTime.Now; + RequestState pubnubRequestState = new RequestState(); + pubnubRequestState.Channels = channels; + pubnubRequestState.ChannelGroups = channelGroups; + pubnubRequestState.ResponseType = responseType; + pubnubRequestState.Timetoken = timetoken.GetValueOrDefault(); + pubnubRequestState.Region = region.GetValueOrDefault(); + pubnubRequestState.TimeQueued = DateTime.Now; - // Wait for message - - await UrlProcessRequest(request, pubnubRequestState, false).ContinueWith(r => - { - resp = r.Result; - }, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false); - } - catch(Exception ex) + Tuple responseTuple = await UrlProcessRequest(request, pubnubRequestState, false).ConfigureAwait(false); + if (!string.IsNullOrEmpty(responseTuple.Item1) && responseTuple.Item2 == null) { - LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager=> MultiChannelSubscribeInit \n channel(s)={1} \n cg(s)={2} \n Exception Details={3}", DateTime.Now.ToString(CultureInfo.InvariantCulture), string.Join(",", channels.OrderBy(x => x).ToArray()), string.Join(",", channelGroups.OrderBy(x => x).ToArray()), ex), config.LogVerbosity); - } - return resp; + PNStatus status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, channels, channelGroups); + HandshakeResponse handshakeResponse = JsonConvert.DeserializeObject(responseTuple.Item1); + return new Tuple(handshakeResponse, status); + } + + return new Tuple(null, responseTuple.Item2); } internal void HandshakeRequestCancellation() @@ -151,9 +145,9 @@ internal void HandshakeRequestCancellation() LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0} SubscribeManager => HandshakeRequestCancellation. No request to cancel.", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.LogVerbosity); } } - internal async Task> ReceiveRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) + internal async Task, PNStatus>> ReceiveRequest(PNOperationType responseType, string[] channels, string[] channelGroups, long? timetoken, int? region, Dictionary initialSubscribeUrlParams, Dictionary externalQueryParam) { - Tuple resp = new Tuple ("", null); + Tuple, PNStatus> resp = new Tuple, PNStatus>(null, null); try { @@ -162,7 +156,7 @@ internal async Task> ReceiveRequest(PNOperationType r IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, null, ""); Uri request = urlBuilder.BuildMultiChannelSubscribeRequest("GET", "", channels, channelGroups, timetoken.GetValueOrDefault(), region.GetValueOrDefault(), channelsJsonState, initialSubscribeUrlParams, externalQueryParam); - RequestState pubnubRequestState = new RequestState(); + RequestState> pubnubRequestState = new RequestState>(); pubnubRequestState.Channels = channels; pubnubRequestState.ChannelGroups = channelGroups; pubnubRequestState.ResponseType = responseType; @@ -172,11 +166,18 @@ internal async Task> ReceiveRequest(PNOperationType r pubnubRequestState.TimeQueued = DateTime.Now; // Wait for message - - await UrlProcessRequest(request, pubnubRequestState, false).ContinueWith(r => + var responseTuple = await UrlProcessRequest(request, pubnubRequestState, false).ConfigureAwait(false); + if (!string.IsNullOrEmpty(responseTuple.Item1) && responseTuple.Item2 == null) { - resp = r.Result; - }, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false); + PNStatus status = new PNStatus(null, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNConnectedCategory, channels, channelGroups); + 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) { @@ -255,29 +256,10 @@ internal protected async Task> UrlProcessRequest(Uri try { - if (terminateCurrentSubRequest) - { - //TerminateCurrentSubscriberRequest(); - } - - //if (PubnubInstance == null) - //{ - // System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "DateTime {0}, PubnubInstance is null. Exiting UrlProcessRequest", DateTime.Now.ToString(CultureInfo.InvariantCulture))); - // return new Tuple("", null); - //} - if (pubnubRequestState != null) { channel = (pubnubRequestState.Channels != null && pubnubRequestState.Channels.Length > 0) ? string.Join(",", pubnubRequestState.Channels.OrderBy(x => x).ToArray()) : ","; - //if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && !channel.Equals(",", StringComparison.OrdinalIgnoreCase) && !ChannelRequest[PubnubInstance.InstanceId].ContainsKey(channel) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) - //{ - // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - // { - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, UrlProcessRequest ChannelRequest PubnubInstance.InstanceId Channel NOT matching", DateTime.Now.ToString(CultureInfo.InvariantCulture)), currentConfig.LogVerbosity); - // } - // return new Tuple("", null); - //} } #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 @@ -299,23 +281,8 @@ internal protected async Task> UrlProcessRequest(Uri pubnubRequestState.Request = request; httpSubscribe = request; - - //if (ChannelRequest.ContainsKey(PubnubInstance.InstanceId) && (pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation || pubnubRequestState.ResponseType == PNOperationType.Presence)) - //{ - // ChannelRequest[PubnubInstance.InstanceId].AddOrUpdate(channel, pubnubRequestState.Request, (key, oldState) => pubnubRequestState.Request); - //} #endif - //if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - //{ - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, Request={1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), requestUri.ToString()), currentConfig.LogVerbosity); - //} - - //if (pubnubRequestState != null && pubnubRequestState.ResponseType == PNOperationType.PNSubscribeOperation) - //{ - // SubscribeRequestTracker.AddOrUpdate(PubnubInstance.InstanceId, DateTime.Now, (key, oldState) => DateTime.Now); - //} - string jsonString = ""; #if !NET35 && !NET40 && !NET45 && !NET461 && !NET48 && !NETSTANDARD10 if (pubnubRequestState != null && pubnubRequestState.UsePostMethod) @@ -344,31 +311,12 @@ internal protected async Task> UrlProcessRequest(Uri jsonString = await pubnubHttp.SendRequestAndGetJsonResponse(requestUri, pubnubRequestState, request).ConfigureAwait(false); } #endif - - //if (SubscribeDisconnected.ContainsKey(PubnubInstance.InstanceId) && SubscribeDisconnected[PubnubInstance.InstanceId]) - //{ - // if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - // { - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0},Received JSON but SubscribeDisconnected = {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonString, requestUri), currentConfig.LogVerbosity); - // } - // throw new OperationCanceledException("Disconnected"); - //} - - //if (pubnubConfig.TryGetValue(PubnubInstance.InstanceId, out currentConfig) && pubnubLog.TryGetValue(PubnubInstance.InstanceId, out currentLog)) - //{ - // LoggingMethod.WriteToLog(currentLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, JSON= {1} for request={2}", DateTime.Now.ToString(CultureInfo.InvariantCulture), jsonString, requestUri), currentConfig.LogVerbosity); - //} - PNStatus errStatus = GetStatusIfError(pubnubRequestState, jsonString); - if (errStatus == null && pubnubRequestState != null) - { - PNStatus status = new StatusBuilder(config, jsonLibrary).CreateStatusResponse(pubnubRequestState.ResponseType, PNStatusCategory.PNAcknowledgmentCategory, pubnubRequestState, (int)HttpStatusCode.OK, null); - return new Tuple(jsonString, status); - } - else + if (pubnubLog != null && config != null) { - jsonString = ""; - return new Tuple(jsonString, errStatus); + 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); } catch (Exception ex) { @@ -434,29 +382,6 @@ protected string BuildJsonUserState(string channel, string channelGroup, bool lo throw new ArgumentException("BuildJsonUserState takes either channel or channelGroup at one time. Send one at a time by passing empty value for other."); } - //if (local) - //{ - // if (!string.IsNullOrEmpty(channel) && ChannelLocalUserState[PubnubInstance.InstanceId].ContainsKey(channel)) - // { - // ChannelLocalUserState[PubnubInstance.InstanceId].TryGetValue(channel, out channelUserStateDictionary); - // } - // if (!string.IsNullOrEmpty(channelGroup) && ChannelGroupLocalUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) - // { - // ChannelGroupLocalUserState[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupUserStateDictionary); - // } - //} - //else - //{ - // if (!string.IsNullOrEmpty(channel) && ChannelUserState.ContainsKey(PubnubInstance.InstanceId) && ChannelUserState[PubnubInstance.InstanceId].ContainsKey(channel)) - // { - // ChannelUserState[PubnubInstance.InstanceId].TryGetValue(channel, out channelUserStateDictionary); - // } - // if (!string.IsNullOrEmpty(channelGroup)&& ChannelGroupUserState.ContainsKey(PubnubInstance.InstanceId) && ChannelGroupUserState[PubnubInstance.InstanceId].ContainsKey(channelGroup)) - // { - // ChannelGroupUserState[PubnubInstance.InstanceId].TryGetValue(channelGroup, out channelGroupUserStateDictionary); - // } - //} - StringBuilder jsonStateBuilder = new StringBuilder(); if (channelUserStateDictionary != null) @@ -593,6 +518,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")) { @@ -633,167 +563,19 @@ 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() { - //if (SubscribeDisconnected[PubnubInstance.InstanceId]) - //{ - // return false; - //} - //LoggingMethod.WriteToLog(pubnubLog, string.Format(CultureInfo.InvariantCulture, "DateTime {0}, SubscribeManager Manual Disconnect", DateTime.Now.ToString(CultureInfo.InvariantCulture)), config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId].LogVerbosity : PNLogVerbosity.NONE); - //SubscribeDisconnected[PubnubInstance.InstanceId] = true; - //TerminateCurrentSubscriberRequest(); - //PubnubCoreBase2.TerminatePresenceHeartbeatTimer(); - //TerminateReconnectTimer(); - return true; } - private void RegisterPresenceHeartbeatTimer(string[] channels, string[] channelGroups) - { - //if (PresenceHeartbeatTimer != null) - //{ - // try - // { - // PresenceHeartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite); - // PresenceHeartbeatTimer.Dispose(); - // PresenceHeartbeatTimer = null; - // } - // catch { /* ignore */ } - //} - //if ((channels != null && channels.Length > 0 && channels.Where(s => s != null && s.Contains("-pnpres") == false).Any()) - // || (channelGroups != null && channelGroups.Length > 0 && channelGroups.Where(s => s != null && s.Contains("-pnpres") == false).Any())) - //{ - // RequestState presenceHeartbeatState = new RequestState(); - // presenceHeartbeatState.Channels = channels; - // presenceHeartbeatState.ChannelGroups = channelGroups; - // presenceHeartbeatState.ResponseType = PNOperationType.PNHeartbeatOperation; - // presenceHeartbeatState.Request = null; - // presenceHeartbeatState.Response = null; - - // if (config.ContainsKey(PubnubInstance.InstanceId) && config[PubnubInstance.InstanceId].PresenceInterval > 0) - // { - // PresenceHeartbeatTimer = new Timer(OnPresenceHeartbeatIntervalTimeout, presenceHeartbeatState, config[PubnubInstance.InstanceId].PresenceInterval * 1000, config[PubnubInstance.InstanceId].PresenceInterval * 1000); - // } - //} - } - -#pragma warning disable - void OnPresenceHeartbeatIntervalTimeout(System.Object presenceHeartbeatState) -#pragma warning restore - { - ////Make presence heartbeat call - //RequestState currentState = presenceHeartbeatState as RequestState; - //if (currentState != null) - //{ - // string[] subscriberChannels = (currentState.Channels != null) ? currentState.Channels.Where(s => s.Contains("-pnpres") == false).ToArray() : null; - // string[] subscriberChannelGroups = (currentState.ChannelGroups != null) ? currentState.ChannelGroups.Where(s => s.Contains("-pnpres") == false).ToArray() : null; - - // bool networkConnection = CheckInternetConnectionStatus(PubnetSystemActive, currentState.ResponseType, currentState.PubnubCallback, currentState.Channels, currentState.ChannelGroups); - // if (networkConnection) - // { - // if ((subscriberChannels != null && subscriberChannels.Length > 0) || (subscriberChannelGroups != null && subscriberChannelGroups.Length > 0)) - // { - // string channelsJsonState = BuildJsonUserState(subscriberChannels, subscriberChannelGroups, false); - // IUrlRequestBuilder urlBuilder = new UrlRequestBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary, unit, pubnubLog, pubnubTelemetryMgr, (PubnubInstance != null && !string.IsNullOrEmpty(PubnubInstance.InstanceId) && PubnubTokenMgrCollection.ContainsKey(PubnubInstance.InstanceId)) ? PubnubTokenMgrCollection[PubnubInstance.InstanceId] : null, (PubnubInstance != null) ? PubnubInstance.InstanceId : ""); - - // Uri request = urlBuilder.BuildPresenceHeartbeatRequest("GET", "", subscriberChannels, subscriberChannelGroups, channelsJsonState); - - // RequestState requestState = new RequestState(); - // requestState.Channels = currentState.Channels; - // requestState.ChannelGroups = currentState.ChannelGroups; - // requestState.ResponseType = PNOperationType.PNHeartbeatOperation; - // requestState.PubnubCallback = null; - // requestState.Reconnect = false; - // requestState.Response = null; - // requestState.TimeQueued = DateTime.Now; - - // UrlProcessRequest(request, requestState, false).ContinueWith(r => - // { - // string json = r.Result.Item1; - // if (!string.IsNullOrEmpty(json)) - // { - // List result = ProcessJsonResponse(requestState, json); - // //ProcessResponseCallbacks(result, requestState); - // } - // }, TaskContinuationOptions.ExecuteSynchronously).Wait(); - // } - // } - // else - // { - // if (PubnubInstance != null && !networkConnection) - // { - // PNStatus status = new StatusBuilder(config.ContainsKey(PubnubInstance.InstanceId) ? config[PubnubInstance.InstanceId] : null, jsonLibrary).CreateStatusResponse(PNOperationType.PNSubscribeOperation, PNStatusCategory.PNNetworkIssuesCategory, null, (int)System.Net.HttpStatusCode.NotFound, new PNException("Internet connection problem during presence heartbeat.")); - // if (subscriberChannels != null && subscriberChannels.Length > 0) - // { - // status.AffectedChannels.AddRange(subscriberChannels.ToList()); - // } - // if (subscriberChannelGroups != null && subscriberChannelGroups.Length > 0) - // { - // status.AffectedChannelGroups.AddRange(subscriberChannelGroups.ToList()); - // } - // Announce(status); - // } - - // } - //} - - } #region IDisposable Support private bool disposedValue; diff --git a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs b/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs deleted file mode 100644 index 74bef4f69..000000000 --- a/src/Api/PubnubApi/EndPoint/PubSub/SubscribeOperation2.cs +++ /dev/null @@ -1,869 +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.EventEngine.Subscribe.Events; -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 PresenceOperation presenceOperation; - private Dictionary queryParam; - private PubnubEventEngine.EventEngine pnEventEngine; - 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, PresenceOperation presenceOperation) - { - PubnubInstance = instance; - config = pubnubConfig; - jsonLibrary = jsonPluggableLibrary; - unit = pubnubUnit; - pubnubLog = log; - pubnubTelemetryMgr = telemetryManager; - pubnubTokenMgr = tokenManager; - this.subscribeEventEngineFactory = subscribeEventEngineFactory; - this.instanceId = instanceId; - this.presenceOperation = presenceOperation; - - var eventEmitter = new EventEmitter(); - eventEmitter.RegisterJsonListener(JsonCallback); - - var handshakeEffectHandler = new HandshakeEffectHandler(eventEmitter); - handshakeEffectHandler.LogCallback = LogCallback; - handshakeEffectHandler.HandshakeRequested += HandshakeEffect_HandshakeRequested; - handshakeEffectHandler.CancelHandshakeRequested += HandshakeEffect_CancelHandshakeRequested; - handshakeEffectHandler.AnnounceStatus = Announce; - - var handshakeReconnectEffectHandler = new HandshakeReconnectEffectHandler(eventEmitter); - handshakeReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; - handshakeReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; - handshakeReconnectEffectHandler.LogCallback = LogCallback; - handshakeReconnectEffectHandler.HandshakeReconnectRequested += HandshakeReconnectEffect_HandshakeRequested; - handshakeReconnectEffectHandler.CancelHandshakeReconnectRequested += HandshakeReconnectEffect_CancelHandshakeRequested; - handshakeReconnectEffectHandler.AnnounceStatus = Announce; - - var handshakeFailedEffectHandler = new HandshakeFailedEffectHandler(eventEmitter); - handshakeFailedEffectHandler.LogCallback = LogCallback; - - var receivingEffectHandler = new ReceivingEffectHandler(eventEmitter); - receivingEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; - receivingEffectHandler.LogCallback = LogCallback; - receivingEffectHandler.ReceiveRequested += ReceivingEffect_ReceiveRequested; - receivingEffectHandler.CancelReceiveRequested += ReceivingEffect_CancelReceiveRequested; - receivingEffectHandler.AnnounceStatus = Announce; - receivingEffectHandler.AnnounceMessage = Announce; - receivingEffectHandler.AnnouncePresenceEvent = Announce; - - var receiveReconnectEffectHandler = new ReceiveReconnectingEffectHandler(eventEmitter); - receiveReconnectEffectHandler.ReconnectionPolicy = config.ReconnectionPolicy; - receiveReconnectEffectHandler.MaxRetries = config.ConnectionMaxRetries; - receiveReconnectEffectHandler.LogCallback = LogCallback; - receiveReconnectEffectHandler.ReceiveReconnectRequested += ReceiveReconnectEffect_ReceiveRequested; - receiveReconnectEffectHandler.CancelReceiveReconnectRequested += ReceiveReconnectEffect_CancelReceiveRequested; - receiveReconnectEffectHandler.AnnounceStatus = Announce; - - var effectDispatcher = new EffectDispatcher(); - effectDispatcher.PubnubUnitTest = unit; - effectDispatcher.Register(EventType.Handshake,handshakeEffectHandler); - effectDispatcher.Register(EventType.CancelHandshake,handshakeEffectHandler); - effectDispatcher.Register(EventType.HandshakeSuccess, handshakeEffectHandler); - - effectDispatcher.Register(EventType.HandshakeFailure, handshakeFailedEffectHandler); - effectDispatcher.Register(EventType.CancelHandshakeFailure, handshakeFailedEffectHandler); - - effectDispatcher.Register(EventType.HandshakeReconnect, handshakeReconnectEffectHandler); - effectDispatcher.Register(EventType.CancelHandshakeReconnect, handshakeReconnectEffectHandler); - effectDispatcher.Register(EventType.HandshakeReconnectSuccess, handshakeReconnectEffectHandler); - effectDispatcher.Register(EventType.HandshakeReconnectGiveUp, handshakeReconnectEffectHandler); - - effectDispatcher.Register(EventType.ReceiveMessages, receivingEffectHandler); - effectDispatcher.Register(EventType.CancelReceiveMessages, receivingEffectHandler); - effectDispatcher.Register(EventType.ReceiveSuccess, receivingEffectHandler); - - effectDispatcher.Register(EventType.ReceiveReconnect, receiveReconnectEffectHandler); - effectDispatcher.Register(EventType.CancelReceiveReconnect, receiveReconnectEffectHandler); - effectDispatcher.Register(EventType.ReceiveReconnectSuccess, receiveReconnectEffectHandler); - effectDispatcher.Register(EventType.ReceiveReconnectGiveUp, receiveReconnectEffectHandler); - - // pnEventEngine = new EventEngine(effectDispatcher, eventEmitter); - // pnEventEngine.PubnubUnitTest = unit; - // pnEventEngine.Setup(config); - - // if (pnEventEngine.PubnubUnitTest != null) - // { - // pnEventEngine.PubnubUnitTest.EventTypeList = new List>(); - // } - // else - // { - //pnEventEngine.InitialState(new State(StateType.Unsubscribed) { EventType = EventType.SubscriptionChanged }); - // } - } - - private void ReceivingEffect_ReceiveRequested(object sender, ReceiveRequestEventArgs e) - { - Tuple resp = manager.ReceiveRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.ReceiveResponseCallback?.Invoke(jsonResp); - } - - private void HandshakeEffect_HandshakeRequested(object sender, HandshakeRequestEventArgs e) - { - Tuple resp = manager.HandshakeRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), 0, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.HandshakeResponseCallback?.Invoke(jsonResp); - } - - private void HandshakeReconnectEffect_HandshakeRequested(object sender, HandshakeReconnectRequestEventArgs e) - { - Tuple resp = manager.HandshakeRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), 0, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.HandshakeReconnectResponseCallback?.Invoke(jsonResp); - } - private void HandshakeEffect_CancelHandshakeRequested(object sender, CancelHandshakeRequestEventArgs e) - { - manager.HandshakeRequestCancellation(); - } - private void HandshakeReconnectEffect_CancelHandshakeRequested(object sender, CancelHandshakeReconnectRequestEventArgs e) - { - manager.HandshakeRequestCancellation(); - } - private void ReceivingEffect_CancelReceiveRequested(object sender, CancelReceiveRequestEventArgs e) - { - manager.ReceiveRequestCancellation(); - } - private void ReceiveReconnectEffect_ReceiveRequested(object sender, ReceiveReconnectRequestEventArgs e) - { - Tuple resp = manager.ReceiveRequest(PNOperationType.PNSubscribeOperation, e.ExtendedState.Channels.ToArray(), e.ExtendedState.ChannelGroups.ToArray(), e.ExtendedState.Timetoken, e.ExtendedState.Region, null, null).Result; - - string jsonResp = resp.Item1; - e.ReceiveReconnectResponseCallback?.Invoke(jsonResp); - } - private void ReceiveReconnectEffect_CancelReceiveRequested(object sender, CancelReceiveReconnectRequestEventArgs e) - { - manager.ReceiveReconnectRequestCancellation(); - } - - private void JsonCallback(string json, bool zeroTimeTokenRequest, int messageCount) - { - if (!string.IsNullOrEmpty(json)) - { - List respObject = manager.WrapResultBasedOnResponseType(PNOperationType.PNSubscribeOperation, json, pnEventEngine.Context.Channels.ToArray(), pnEventEngine.Context.ChannelGroups.ToArray()); - if (respObject != null && respObject.Count > 0) - { - ProcessListenerCallback(respObject, zeroTimeTokenRequest, messageCount, pnEventEngine.Context.Channels.ToArray(), pnEventEngine.Context.ChannelGroups.ToArray()); - } - } - } - - protected void ProcessListenerCallback(List result, bool zeroTimeTokenRequest, int messageCount, string[] channels, string[] channelGroups) - { - 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); - - } - if(channels!=null && channels.Length > 0) subscribeEventEngine.Channels = subscribeEventEngine.Channels.Concat(channels).ToArray(); - if(channelGroups!=null && channelGroups.Length > 0)subscribeEventEngine.Channelgroups = subscribeEventEngine.Channelgroups.Concat(channelGroups).ToArray(); - subscribeEventEngine.eventQueue.Enqueue(new SubscriptionChangedEvent() { Channels = subscribeEventEngine.Channels, ChannelGroups = subscribeEventEngine.Channelgroups }); - if (this.presenceOperation != null) - { - this.presenceOperation.Start(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) - { - foreach (var listener in SubscribeListenerList) { - listener.Status(pubnubInstance, status); - } - } - - } -} diff --git a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeAllEndpoint.cs b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeAllEndpoint.cs new file mode 100644 index 000000000..c483698e4 --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeAllEndpoint.cs @@ -0,0 +1,56 @@ +using PubnubApi.EventEngine.Presence; +using PubnubApi.EventEngine.Subscribe; + +namespace PubnubApi.EndPoint +{ + public class UnsubscribeAllEndpoint: UnsubscribeAllOperation + { + private readonly PNConfiguration config; + private readonly IJsonPluggableLibrary jsonLibrary; + private readonly IPubnubUnitTest unit; + private readonly IPubnubLog pubnubLog; + private readonly EndPoint.TelemetryManager pubnubTelemetryManager; + private readonly EndPoint.TokenManager pubnubTokenManager; + private readonly SubscribeEventEngineFactory subscribeEventEngineFactory; + private readonly PresenceEventEngineFactory presenceEventEngineFactory; + private readonly string instanceId; + + public UnsubscribeAllEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, SubscribeEventEngineFactory subscribeEventEngineFactory, PresenceEventEngineFactory presenceEventEngineFactory ,Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) + { + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryManager = telemetryManager; + pubnubTokenManager = tokenManager; + this.subscribeEventEngineFactory = subscribeEventEngineFactory; + this.presenceEventEngineFactory = presenceEventEngineFactory; + instanceId = PubnubInstance.InstanceId; + UnsubscribeAll(); + } + + + private void UnsubscribeAll() + { + if (subscribeEventEngineFactory.HasEventEngine(instanceId)) { + SubscribeEventEngine subscribeEventEngine = subscribeEventEngineFactory.GetEventEngine(instanceId); + subscribeEventEngine.UnsubscribeAll(); + } + + if (config.PresenceInterval > 0 && presenceEventEngineFactory.HasEventEngine(instanceId)) { + PresenceEventEngine presenceEventEngine = presenceEventEngineFactory.GetEventEngine(instanceId); + presenceEventEngine.EventQueue.Enqueue(new EventEngine.Presence.Events.LeftAllEvent()); + } + + if (config.MaintainPresenceState) { + if (ChannelLocalUserState.TryGetValue(instanceId, out var channelStates)) { + channelStates.Clear(); + } + if (ChannelLocalUserState.TryGetValue(instanceId, out var channelGroupStates)) { + channelGroupStates.Clear(); + } + } + } + } +} + diff --git a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs new file mode 100644 index 000000000..4417ed2a3 --- /dev/null +++ b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeEndpoint.cs @@ -0,0 +1,105 @@ +using PubnubApi.EventEngine.Presence; +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 : PubnubCoreBase, 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 PresenceEventEngineFactory presenceEventEngineFactory; + private string instanceId { get; set; } + + public UnsubscribeEndpoint(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, SubscribeEventEngineFactory subscribeEventEngineFactory, PresenceEventEngineFactory presenceEventEngineFactory, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) + { + pubnubInstance = instance; + config = pubnubConfig; + jsonLibrary = jsonPluggableLibrary; + unit = pubnubUnit; + pubnubLog = log; + pubnubTelemetryMgr = telemetryManager; + pubnubTokenMgr = tokenManager; + this.subscribeEventEngineFactory = subscribeEventEngineFactory; + this.presenceEventEngineFactory = presenceEventEngineFactory; + 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); + } + if (config.PresenceInterval > 0 && presenceEventEngineFactory.HasEventEngine(instanceId)) { + PresenceEventEngine presenceEventEngine = presenceEventEngineFactory.GetEventEngine(instanceId); + presenceEventEngine.EventQueue.Enqueue(new EventEngine.Presence.Events.LeftEvent() { Input = new EventEngine.Presence.Common.PresenceInput() { Channels = channels, ChannelGroups = channelGroups } }); + } + if (config.MaintainPresenceState) { + if (ChannelLocalUserState.TryGetValue(PubnubInstance.InstanceId, out + var userState)) { + foreach (var channelName in channels ?? new string[0]) { + userState.TryRemove(channelName, out _); + } + } + if (ChannelGroupLocalUserState.TryGetValue(PubnubInstance.InstanceId, out + var channelGroupUserState)) { + foreach (var channelGroupName in channelGroups ?? new string[0]) { + channelGroupUserState.TryRemove(channelGroupName, out _); + } + } + } + } + } +} diff --git a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs index fc3f950ea..1bbc9990a 100644 --- a/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs +++ b/src/Api/PubnubApi/EndPoint/PubSub/UnsubscribeOperation.cs @@ -5,15 +5,14 @@ using System.Net; using System.Threading; using System.Threading.Tasks; -using PubnubApi.EventEngine.Subscribe; -using PubnubApi.EventEngine.Subscribe.Events; +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; @@ -25,9 +24,8 @@ public class UnsubscribeOperation : PubnubCoreBase private string[] subscribeChannelNames; private string[] subscribeChannelGroupNames; private Dictionary queryParam; - private SubscribeEventEngineFactory subscribeEventEngineFactory; - public UnsubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance, SubscribeEventEngineFactory subscribeEventEngineFactory) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) + public UnsubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary jsonPluggableLibrary, IPubnubUnitTest pubnubUnit, IPubnubLog log, EndPoint.TelemetryManager telemetryManager, EndPoint.TokenManager tokenManager, Pubnub instance) : base(pubnubConfig, jsonPluggableLibrary, pubnubUnit, log, telemetryManager, tokenManager, instance) { config = pubnubConfig; jsonLibrary = jsonPluggableLibrary; @@ -35,22 +33,21 @@ public UnsubscribeOperation(PNConfiguration pubnubConfig, IJsonPluggableLibrary pubnubLog = log; pubnubTelemetryMgr = telemetryManager; pubnubTokenMgr = tokenManager; - this.subscribeEventEngineFactory = subscribeEventEngineFactory; } - 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; @@ -58,30 +55,7 @@ public UnsubscribeOperation QueryParam(Dictionary customQuery public void Execute() { - if (config.EnableEventEngine && subscribeEventEngineFactory != null) { - if (subscribeEventEngineFactory.hasEventEngine(PubnubInstance.InstanceId)) { - var subscribeEventEngine = subscribeEventEngineFactory.getEventEngine(PubnubInstance.InstanceId); - subscribeEventEngine.Channels = subscribeEventEngine.Channels.Except(this.subscribeChannelNames).ToArray(); - subscribeEventEngine.Channelgroups = subscribeEventEngine.Channelgroups.Except(this.subscribeChannelGroupNames).ToArray(); - subscribeEventEngine.eventQueue.Enqueue(new SubscriptionChangedEvent() { - Channels = subscribeEventEngine.Channels, ChannelGroups = subscribeEventEngine.Channelgroups - }); - if (ChannelLocalUserState.TryGetValue(PubnubInstance.InstanceId, out var userState)) { - foreach (var channel in this.subscribeChannelNames ?? new string[0]) { - userState.TryRemove(channel, out _); - } - } - if (ChannelGroupLocalUserState.TryGetValue(PubnubInstance.InstanceId, out var channelGroupUserState)) { - foreach (var channelGroup in this.subscribeChannelGroupNames ?? new string[0]) { - channelGroupUserState.TryRemove(channelGroup, out _); - } - } - } - } - else { Unsubscribe(subscribeChannelNames, subscribeChannelGroupNames); - } - } private void Unsubscribe(string[] channels, string[] channelGroups) diff --git a/src/Api/PubnubApi/EventEngine/Common/Delay.cs b/src/Api/PubnubApi/EventEngine/Common/Delay.cs index 3afb9a4a6..6459033e3 100644 --- a/src/Api/PubnubApi/EventEngine/Common/Delay.cs +++ b/src/Api/PubnubApi/EventEngine/Common/Delay.cs @@ -43,7 +43,7 @@ private void AwaiterLoop() { if (Cancelled) { - taskCompletionSource.SetCanceled(); + taskCompletionSource.TrySetCanceled(); break; } Monitor.Wait(monitor, milliseconds); diff --git a/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs b/src/Api/PubnubApi/EventEngine/Core/EffectDispatcher.cs index 1a326159e..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)) - handler.Run(invocation).Start(); + 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 e2062127a..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; @@ -32,31 +35,39 @@ public event System.Action OnEffectDispatch public event System.Action OnEventQueued; public Engine() { - eventQueue.OnEventQueued += OnEvent; + EventQueue.OnEventQueued += OnEvent; } ~Engine() { - eventQueue.OnEventQueued -= OnEvent; + EventQueue.OnEventQueued -= OnEvent; } private async void OnEvent(EventQueue q) { OnEventQueued?.Invoke(q.Peek()); - await currentTransitionLoop; - currentTransitionLoop = eventQueue.Loop(async e => currentState = await Transition(e)); + 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/Events/PresenceEvents.cs b/src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs index 3bc25cfcc..559f502c1 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs @@ -5,31 +5,46 @@ namespace PubnubApi.EventEngine.Presence.Events public class JoinedEvent : Core.IEvent { public PresenceInput Input { get; set; } + public string Name { get; set; } = "JOINED"; } public class LeftEvent : Core.IEvent { public PresenceInput Input { get; set; } + public string Name { get; set; } = "LEFT"; } - public class LeftAllEvent : Core.IEvent { } + public class LeftAllEvent : Core.IEvent { + public string Name { get; set; } = "LEFT_ALL"; + } - public class HeartbeatSuccessEvent : Core.IEvent { } + public class HeartbeatSuccessEvent : Core.IEvent { + public virtual string Name { get; set; } = "HEARTBEAT_SUCCESS"; + } public class HeartbeatFailureEvent : Core.IEvent { public PNStatus Status { get; set; } public int retryCount; + public virtual string Name { get; set; } = "HEARTBEAT_FAILURE"; } public class HeartbeatGiveUpEvent : Core.IEvent { public PNStatus Status { get; set; } + public string Name { get; set; } = "HEARTBEAT_GIVEUP"; } - public class ReconnectEvent : Core.IEvent { } + public class ReconnectEvent : Core.IEvent { + public PresenceInput Input { get; set; } + public string Name { get; set; } = "RECONNECT"; + } - public class DisconnectEvent : Core.IEvent { } + public class DisconnectEvent : Core.IEvent { + public string Name { get; set; } = "DISCONNECT"; + } - public class TimesUpEvent : Core.IEvent { } + public class TimesUpEvent : Core.IEvent { + public string Name { get; set; } = "TIMES_UP"; + } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs index 047339b0d..efb1b896f 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelDelayedHeartbeatInvocation.cs @@ -1,4 +1,6 @@ namespace PubnubApi.EventEngine.Presence.Invocations { - public class CancelDelayedHeartbeatInvocation : Core.IEffectCancelInvocation { } + public class CancelDelayedHeartbeatInvocation : DelayedHeartbeatInvocation, Core.IEffectCancelInvocation { + public override string Name { get; set; } = "CANCEL_DELAYED_HEARTBEAT"; + } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs index 671d9313d..458ac465d 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/CancelWaitInvocation.cs @@ -2,5 +2,7 @@ namespace PubnubApi.EventEngine.Presence.Invocations { - public class CancelWaitInvocation : Core.IEffectCancelInvocation {} + public class CancelWaitInvocation : WaitInvocation, Core.IEffectCancelInvocation { + public override string Name { get; set; } = "CANCEL_WAIT"; + } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs index ff18bb9f1..e98d4e4bc 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/DelayedHeartbeatInvocation.cs @@ -7,5 +7,6 @@ public class DelayedHeartbeatInvocation : Core.IEffectInvocation public PresenceInput Input { get; set; } public int RetryCount { get; set; } public PNStatus Reason { get; set; } + public virtual string Name { get; set; } = "DELAYED_HEARTBEAT"; } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/Dummy.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/Dummy.cs deleted file mode 100644 index 0e6059c0b..000000000 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/Dummy.cs +++ /dev/null @@ -1,4 +0,0 @@ -// TODO: Dummy Invocation until we have real ones -namespace PubnubApi.EventEngine.Presence.Invocations { - public class DummyInvocation : Core.IEffectInvocation {} -} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs index 1253413e4..b8d416b2c 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/HearbeatInvocation.cs @@ -5,5 +5,6 @@ namespace PubnubApi.EventEngine.Presence.Invocations public class HeartbeatInvocation : Core.IEffectInvocation { public PresenceInput Input { get; set; } + public virtual string Name { get; set; } = "HEARTBEAT"; } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs index c6c4a6a1c..81380b0e0 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/LeaveInvocation.cs @@ -5,5 +5,6 @@ namespace PubnubApi.EventEngine.Presence.Invocations public class LeaveInvocation : Core.IEffectInvocation { public PresenceInput Input { get; set; } + public virtual string Name { get; set; } = "LEAVE"; } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/TerminateEventEngineInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/TerminateEventEngineInvocation.cs deleted file mode 100644 index 82c797480..000000000 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/TerminateEventEngineInvocation.cs +++ /dev/null @@ -1,6 +0,0 @@ -using PubnubApi.EventEngine.Core; - -namespace PubnubApi.EventEngine.Presence.Invocations -{ - public class TerminateEventEngineInvocation : Core.IEffectInvocation {} -} diff --git a/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs b/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs index d013fccb4..ea0821d1f 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/Invocations/WaitInvocation.cs @@ -5,5 +5,6 @@ namespace PubnubApi.EventEngine.Presence.Invocations public class WaitInvocation : Core.IEffectInvocation { public PresenceInput Input { get; set; } + public virtual string Name { get; set; } = "WAIT"; } } diff --git a/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs index 2f18d0f2f..4a024cc2c 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngine.cs @@ -7,14 +7,14 @@ public class PresenceEventEngine : Engine { public PresenceEventEngine(PNConfiguration pnConfiguration, HeartbeatOperation heartbeatOperation, LeaveOperation leaveOperation) { - var heartbeatEffectHandler = new Effects.HeartbeatEffectHandler(heartbeatOperation, eventQueue); + var heartbeatEffectHandler = new Effects.HeartbeatEffectHandler(heartbeatOperation, EventQueue); dispatcher.Register(heartbeatEffectHandler); - var delayedHeartbeatEffectHandler = new Effects.DelayedHeartbeatEffectHandler(pnConfiguration, heartbeatOperation, eventQueue); + var delayedHeartbeatEffectHandler = new Effects.DelayedHeartbeatEffectHandler(pnConfiguration, heartbeatOperation, EventQueue); dispatcher.Register(delayedHeartbeatEffectHandler); dispatcher.Register(delayedHeartbeatEffectHandler); - var waitEffectHandler = new Effects.WaitEffectHandler(pnConfiguration, eventQueue); + var waitEffectHandler = new Effects.WaitEffectHandler(pnConfiguration, EventQueue); dispatcher.Register(waitEffectHandler); dispatcher.Register(waitEffectHandler); diff --git a/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs index 678d174c2..53afdd87f 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/PresenceEventEngineFactory.cs @@ -12,19 +12,19 @@ public PresenceEventEngineFactory() this.engineInstances = new ConcurrentDictionary(); } - internal bool hasEventEngine(string instanceId) + internal bool HasEventEngine(string instanceId) { return engineInstances.ContainsKey(instanceId); } - internal PresenceEventEngine getEventEngine(string instanceId) + internal PresenceEventEngine GetEventEngine(string instanceId) { PresenceEventEngine subscribeEventEngine; engineInstances.TryGetValue(instanceId, out subscribeEventEngine); return subscribeEventEngine; } - internal PresenceEventEngine initializeEventEngine(string instanceId, + internal PresenceEventEngine InitializeEventEngine(string instanceId, Pubnub pubnubInstance, IPubnubLog pubnubLog, TelemetryManager telemetryManager, TokenManager tokenManager) { HeartbeatOperation heartbeatOperation = new HeartbeatOperation(pubnubInstance.PNConfig, pubnubInstance.JsonPluggableLibrary, pubnubInstance.PubnubUnitTest, pubnubLog, telemetryManager, tokenManager, pubnubInstance); diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs index eabc2aea6..5dbcbaea1 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs @@ -9,10 +9,6 @@ public class FailedState : APresenceState { public PNStatus Reason { get; set; } - // TODO: Dummy Invocation until we have real ones - public override IEnumerable OnEntry => new DummyInvocation().AsArray(); - public override IEnumerable OnExit => new DummyInvocation().AsArray(); - // TODO: transitions public override TransitionResult Transition(IEvent ev) { diff --git a/src/Api/PubnubApi/EventEngine/Presence/States/StoppedState.cs b/src/Api/PubnubApi/EventEngine/Presence/States/StoppedState.cs index 5e8751b43..25fbe7d4d 100644 --- a/src/Api/PubnubApi/EventEngine/Presence/States/StoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Presence/States/StoppedState.cs @@ -7,11 +7,6 @@ namespace PubnubApi.EventEngine.Presence.States { public class StoppedState : APresenceState { - // TODO: Dummy Invocation until we have real ones - public override IEnumerable OnEntry => new DummyInvocation().AsArray(); - public override IEnumerable OnExit => new DummyInvocation().AsArray(); - - // TODO: transitions public override TransitionResult Transition(IEvent ev) { return ev switch 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 910300f57..c4ca56369 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Common/CommonSubscribeTypes.cs @@ -80,13 +80,14 @@ 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; } } \ 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 af2d1f10f..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,32 +15,79 @@ 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; }); + 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; public override Task Cancel() diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs index f61722be4..632c3091b 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/HandshakeEffectHandler.cs @@ -3,74 +3,68 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; using PubnubApi.EndPoint; using PubnubApi.EventEngine.Common; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.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 PNConfiguration pubnubConfiguration; - private SubscribeManager2 manager; - private EventQueue eventQueue; - - private Delay retryDelay = new Delay(0); + private readonly SubscribeManager2 manager; + private readonly EventQueue eventQueue; - internal HandshakeEffectHandler(PNConfiguration pubnubConfiguration, SubscribeManager2 manager, EventQueue eventQueue) + internal HandshakeEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { - this.pubnubConfiguration = pubnubConfiguration; this.manager = manager; this.eventQueue = eventQueue; } - public override async Task Run(HandshakeReconnectInvocation invocation) + public override async Task Run(HandshakeInvocation invocation) { - var retryConfiguration = pubnubConfiguration.RetryConfiguration; - if (retryConfiguration == null) - { - eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); - } - else if (!retryConfiguration.RetryPolicy.ShouldRetry(invocation.AttemptedRetries, invocation.Reason)) + var response = await MakeHandshakeRequest(invocation); + SubscriptionCursor cursor = null; + if (response.Item1 != null) { - eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); + if (invocation.Cursor != null && invocation.Cursor.Timetoken != null) + { + cursor = invocation.Cursor; + } + else if (response.Item1.Timetoken != null) + { + cursor = new SubscriptionCursor() + { + Region = response.Item1.Timetoken.Region, + Timetoken = response.Item1.Timetoken.Timestamp + }; + } } else { - retryDelay = new Delay(retryConfiguration.RetryPolicy.GetDelay(invocation.AttemptedRetries, invocation.Reason, null)); - await retryDelay.Start(); - if (!retryDelay.Cancelled) - await Run((HandshakeInvocation)invocation); + if (invocation.Cursor != null && invocation.Cursor.Timetoken != null) + { + cursor = invocation.Cursor; + } } - } - - public override bool IsBackground(HandshakeReconnectInvocation invocation) - { - return true; - } - - public override async Task Run(HandshakeInvocation invocation) - { - var response = await MakeHandshakeRequest(invocation); switch (invocation) { case Invocations.HandshakeReconnectInvocation reconnectInvocation when response.Item2.Error: - eventQueue.Enqueue(new Events.HandshakeReconnectFailureEvent() { AttemptedRetries = reconnectInvocation.AttemptedRetries + 1, Status = response.Item2}); + 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 = response.Item1, Status = response.Item2 }); + eventQueue.Enqueue(new Events.HandshakeReconnectSuccessEvent() { Cursor = cursor, Status = response.Item2 }); break; case { } when response.Item2.Error: - eventQueue.Enqueue(new Events.HandshakeFailureEvent() { Status = response.Item2}); + eventQueue.Enqueue(new Events.HandshakeFailureEvent() { Status = response.Item2, Cursor = cursor, Channels = invocation.Channels, ChannelGroups = invocation.ChannelGroups}); break; case { }: - eventQueue.Enqueue(new Events.HandshakeSuccessEvent() { Cursor = response.Item1, Status = response.Item2 }); + eventQueue.Enqueue(new Events.HandshakeSuccessEvent() { Cursor = cursor, Status = response.Item2 }); break; } @@ -82,9 +76,9 @@ public override bool IsBackground(HandshakeInvocation invocation) } - private async Task> MakeHandshakeRequest(HandshakeInvocation invocation) + private async Task> MakeHandshakeRequest(HandshakeInvocation invocation) { - var resp = await manager.HandshakeRequest( + return await manager.HandshakeRequest( PNOperationType.PNSubscribeOperation, invocation.Channels.ToArray(), invocation.ChannelGroups.ToArray(), @@ -93,34 +87,69 @@ public override bool IsBackground(HandshakeInvocation invocation) invocation.InitialSubscribeQueryParams, invocation.ExternalQueryParams ); + } + + public override async Task Cancel() + { + manager.HandshakeRequestCancellation(); + } + } + + public class HandshakeReconnectEffectHandler : EffectCancellableHandler + { + private readonly EventQueue eventQueue; + + private HandshakeEffectHandler handshakeEffectHandler; + + private PNConfiguration pubnubConfiguration; + + private Delay retryDelay = new Delay(0); + + + internal HandshakeReconnectEffectHandler(PNConfiguration pubnubConfiguration, EventQueue eventQueue, HandshakeEffectHandler handshakeEffectHandler) + { + this.pubnubConfiguration = pubnubConfiguration; + this.eventQueue = eventQueue; + this.handshakeEffectHandler = handshakeEffectHandler; + } + + public override async Task Run(HandshakeReconnectInvocation invocation) + { + var retryConfiguration = pubnubConfiguration.RetryConfiguration; try { - var handshakeResponse = JsonConvert.DeserializeObject(resp.Item1); - var c = new SubscriptionCursor() + if (retryConfiguration == null) + { + eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(new Exception(""), PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnexpectedDisconnectCategory, invocation.Channels, invocation.ChannelGroups ) }); + } + else if (!retryConfiguration.RetryPolicy.ShouldRetry(invocation.AttemptedRetries, invocation.Reason)) { - Region = handshakeResponse.Timetoken.Region, - Timetoken = handshakeResponse.Timetoken.Timestamp - }; - return new System.Tuple(c, resp.Item2); + eventQueue.Enqueue(new HandshakeReconnectGiveUpEvent() { Status = new PNStatus(new Exception(""), PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnexpectedDisconnectCategory, invocation.Channels, invocation.ChannelGroups ) }); + } + else + { + retryDelay = new Delay(retryConfiguration.RetryPolicy.GetDelay(invocation.AttemptedRetries, invocation.Reason, null)); + await retryDelay.Start(); + if (!retryDelay.Cancelled) + await handshakeEffectHandler.Run(invocation as HandshakeInvocation); + } } - catch (Exception e) + catch (Exception ex) { - return new Tuple(null, new PNStatus(e, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, invocation.Channels, invocation.ChannelGroups)); + Debug.WriteLine(ex); } } + public override bool IsBackground(HandshakeReconnectInvocation invocation) => true; + public override async Task Cancel() { if (!retryDelay.Cancelled) { retryDelay.Cancel(); } - else - { - manager.HandshakeRequestCancellation(); - } + 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 3dc06c59a..a92549b3d 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Effects/ReceivingEffectHandler.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; +using System.Linq; using System.Threading.Tasks; -using Newtonsoft.Json; using PubnubApi.EndPoint; using PubnubApi.EventEngine.Common; using PubnubApi.EventEngine.Core; @@ -14,69 +10,44 @@ namespace PubnubApi.EventEngine.Subscribe.Effects { public class ReceivingEffectHandler: - EffectDoubleCancellableHandler + EffectCancellableHandler { - private PNConfiguration pubnubConfiguration; private SubscribeManager2 manager; private EventQueue eventQueue; - - private Delay retryDelay = new Delay(0); - internal ReceivingEffectHandler(PNConfiguration pubnubConfiguration, SubscribeManager2 manager, EventQueue eventQueue) + internal ReceivingEffectHandler(SubscribeManager2 manager, EventQueue eventQueue) { - this.pubnubConfiguration = pubnubConfiguration; this.manager = manager; this.eventQueue = eventQueue; } - public override Task Run(ReceiveReconnectInvocation invocation) - { - var retryConfiguration = pubnubConfiguration.RetryConfiguration; - if (retryConfiguration == null) - { - eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); - } - else if (!retryConfiguration.RetryPolicy.ShouldRetry(invocation.AttemptedRetries, invocation.Reason)) - { - eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); - } - else - { - retryDelay = new Delay(retryConfiguration.RetryPolicy.GetDelay(invocation.AttemptedRetries, invocation.Reason, null)); - // 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); + if (response.Item1 is null) return; 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; } } @@ -86,40 +57,73 @@ public override bool IsBackground(ReceiveMessagesInvocation invocation) return true; } - private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) + private async Task, PNStatus>> MakeReceiveMessagesRequest(ReceiveMessagesInvocation invocation) { - var resp = await manager.ReceiveRequest( + return await manager.ReceiveRequest>( PNOperationType.PNSubscribeOperation, - invocation.Channels.ToArray(), - invocation.ChannelGroups.ToArray(), + invocation.Channels?.ToArray(), + invocation.ChannelGroups?.ToArray(), invocation.Cursor.Timetoken.Value, invocation.Cursor.Region.Value, invocation.InitialSubscribeQueryParams, invocation.ExternalQueryParams ); + } + + public override async Task Cancel() + { + manager.ReceiveRequestCancellation(); + } + } - try + public class ReceivingReconnectEffectHandler : + EffectCancellableHandler + { + private PNConfiguration pubnubConfiguration; + private EventQueue eventQueue; + private ReceivingEffectHandler receivingEffectHandler; + + private Delay retryDelay = new Delay(0); + + internal ReceivingReconnectEffectHandler(PNConfiguration pubnubConfiguration, EventQueue eventQueue, ReceivingEffectHandler receivingEffectHandler) + { + this.pubnubConfiguration = pubnubConfiguration; + this.eventQueue = eventQueue; + this.receivingEffectHandler = receivingEffectHandler; + } + + public override async Task Run(ReceiveReconnectInvocation invocation) + { + var retryConfiguration = pubnubConfiguration.RetryConfiguration; + if (retryConfiguration == null) { - //TODO: get ReceivingResponse from manager.ReceiveRequest - var receiveResponse = JsonConvert.DeserializeObject>(resp.Item1); - return new System.Tuple, PNStatus>(receiveResponse, resp.Item2); + eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); } - catch (Exception e) + else if (!retryConfiguration.RetryPolicy.ShouldRetry(invocation.AttemptedRetries, invocation.Reason)) { - return new Tuple, PNStatus>(null, new PNStatus(e, PNOperationType.PNSubscribeOperation, PNStatusCategory.PNUnknownCategory, invocation.Channels, invocation.ChannelGroups)); + eventQueue.Enqueue(new ReceiveReconnectGiveUpEvent() { Status = new PNStatus(PNStatusCategory.PNCancelledCategory) }); + } + else + { + retryDelay = new Delay(retryConfiguration.RetryPolicy.GetDelay(invocation.AttemptedRetries, invocation.Reason, null)); + // Run in the background + await retryDelay.Start(); + await receivingEffectHandler.Run(invocation); } } - + + public override bool IsBackground(ReceiveReconnectInvocation invocation) + { + return true; + } + public override async Task Cancel() { if (!retryDelay.Cancelled) { retryDelay.Cancel(); } - else - { - manager.ReceiveRequestCancellation(); - } + await receivingEffectHandler.Cancel(); } } } diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs index 1ca59ae29..a9776544a 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Events/SubscriptionEvents.cs @@ -1,104 +1,101 @@ using System.Collections.Generic; using PubnubApi.EventEngine.Subscribe.Common; -namespace PubnubApi.EventEngine.Subscribe.Events -{ - public class UnsubscribeAllEvent : Core.IEvent - { +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 { get; set; } - public IEnumerable ChannelGroups { get; set; } + + 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 { get; set; } - public IEnumerable ChannelGroups { get; set; } - public SubscriptionCursor Cursor { get; set; } + 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 { get; set; } - public PNStatus Status { get; set; } + 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 PNStatus Status { get; set; } - public int AttemptedRetries { get; set; } + 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 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 { get; set; } - public IEnumerable ChannelGroups { get; set; } + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public override string Name { get; set; } = "HANDSHAKE_RECONNECT_FAILURE"; } - // Do we have this in system description ? - public class HandshakeReconnectRetryEvent : Core.IEvent - { + public class HandshakeReconnectGiveUpEvent : Core.IEvent { + public PNStatus Status; + public string Name { get; set; } = "HANDSHAKE_RECONNECT_GIVEUP"; } - public class HandshakeReconnectGiveUpEvent : Core.IEvent - { - public PNStatus Status { get; set; } + public class ReceiveSuccessEvent : Core.IEvent { + public IEnumerable Channels; + public IEnumerable ChannelGroups; + public ReceivingResponse Messages; + public SubscriptionCursor Cursor; + public PNStatus Status; + public virtual string Name { get; set; } = "RECEIVE_SUCCESS"; } - public class ReceiveSuccessEvent : Core.IEvent - { - public IEnumerable Channels { get; set; } - public IEnumerable ChannelGroups { get; set; } - public ReceivingResponse Messages { get; set; } - public SubscriptionCursor Cursor { get; set; } - public PNStatus Status { get; set; } + public class ReceiveFailureEvent : Core.IEvent { + public PNStatus Status; + public int AttemptedRetries; + public SubscriptionCursor Cursor; + public virtual string Name { get; set; } = "RECEIVE_FAILURE"; } - public class ReceiveFailureEvent : Core.IEvent - { - public PNStatus Status { get; set; } - public int AttemptedRetries { get; set; } - public SubscriptionCursor Cursor { get; set; } + public class ReceiveReconnectSuccessEvent : ReceiveSuccessEvent { + public override string Name { get; set; } = "RECEIVE_RECONNECT_SUCCESS"; } - public class ReceiveReconnectRetry : Core.IEvent - { - } - - public class ReceiveReconnectSuccessEvent : ReceiveSuccessEvent - { - } - - public class ReceiveReconnectFailureEvent : ReceiveFailureEvent - { + public class ReceiveReconnectFailureEvent : ReceiveFailureEvent { + public override string Name { get; set; } = "RECEIVE_RECONNECT_FAILURE"; } - public class ReceiveReconnectGiveUpEvent : Core.IEvent - { - public IEnumerable Channels { get; set; } - public IEnumerable ChannelGroups { get; set; } - public SubscriptionCursor Cursor { get; set; } - public PNStatus Status { get; set; } + public class ReceiveReconnectGiveUpEvent : Core.IEvent { + public IEnumerable Channels; + 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 { get; set; } - public IEnumerable ChannelGroups { get; set; } - public SubscriptionCursor Cursor { get; set; } + 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 { get; set; } - public IEnumerable ChannelGroups { get; set; } - public SubscriptionCursor Cursor { get; set; } + 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 57e631664..22a8cb808 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/Invocations/SubscriptionInvocations.cs @@ -3,10 +3,12 @@ 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; } } @@ -15,6 +17,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) { @@ -38,9 +41,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 @@ -50,27 +55,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 RetryConfiguration RetryConfiguration { get; set; } - public int AttemptedRetries { get; set; } + public int AttemptedRetries; public PNStatus Reason { get; set; } + 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 RetryConfiguration RetryConfiguration { get; set; } - public int AttemptedRetries { get; set; } + public int AttemptedRetries; public PNStatus Reason { get; set; } + 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 ef64e0a09..efebe7280 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeFailedState.cs @@ -1,4 +1,5 @@ -using PubnubApi.EventEngine.Core; +using System.Linq; +using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; namespace PubnubApi.EventEngine.Subscribe.States @@ -16,9 +17,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), }, Events.ReconnectEvent reconnect => new HandshakingState() @@ -26,7 +26,6 @@ public override TransitionResult Transition(IEvent e) Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, Cursor = reconnect.Cursor, - }, Events.SubscriptionRestoredEvent subscriptionRestored => new HandshakingState() @@ -34,7 +33,6 @@ public override TransitionResult Transition(IEvent e) Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor, - }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs index 9595744a9..c3f246c40 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeReconnectingState.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Invocations; @@ -12,6 +13,7 @@ public class HandshakeReconnectingState : SubscriptionState public override IEnumerable OnEntry => new HandshakeReconnectInvocation() { Channels = this.Channels, ChannelGroups = this.ChannelGroups, + Cursor = this.Cursor, AttemptedRetries = this.AttemptedRetries }.AsArray(); public override IEnumerable OnExit { get; } = new CancelHandshakeReconnectInvocation().AsArray(); @@ -23,8 +25,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), }, Events.DisconnectEvent disconnect => new HandshakeStoppedState() { diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs index 35df7101e..9327312d3 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakeStoppedState.cs @@ -1,4 +1,5 @@ -using PubnubApi.EventEngine.Core; +using System.Linq; +using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; namespace PubnubApi.EventEngine.Subscribe.States @@ -16,9 +17,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), }, Events.ReconnectEvent reconnect => new HandshakingState() diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs index 5abaa5721..48aa60b40 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/HandshakingState.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; using PubnubApi.EventEngine.Subscribe.Invocations; @@ -7,10 +8,8 @@ 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(); @@ -24,8 +23,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), }, Events.SubscriptionRestoredEvent subscriptionRestored => new States.HandshakingState() @@ -37,14 +36,16 @@ 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, AttemptedRetries = 0 }.With(new EmitStatusInvocation(handshakeFailure.Status)), Events.DisconnectEvent disconnect => new States.HandshakeStoppedState() { Channels = disconnect.Channels, + Cursor = disconnect.Cursor, ChannelGroups = disconnect.ChannelGroups, }.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 520e1119d..d01a5da24 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveFailedState.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; @@ -6,8 +7,6 @@ namespace PubnubApi.EventEngine.Subscribe.States { public class ReceiveFailedState : SubscriptionState { - public SubscriptionCursor Cursor; - public override IEnumerable OnEntry { get; } public override IEnumerable OnExit { get; } @@ -22,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), Cursor = this.Cursor, }, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs index 34399924e..f590b2c2c 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveReconnectingState.cs @@ -2,15 +2,13 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +using System.Linq; namespace PubnubApi.EventEngine.Subscribe.States { public class ReceiveReconnectingState : SubscriptionState { - public IEnumerable Channels; - public IEnumerable ChannelGroups; - public SubscriptionCursor Cursor; - public int AttemptedRetries; + public int AttemptedRetries { get; set;} public override IEnumerable OnEntry => new ReceiveReconnectInvocation() { @@ -34,10 +32,9 @@ 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, - }, Events.DisconnectEvent disconnect => new ReceiveStoppedState() @@ -62,7 +59,9 @@ public override TransitionResult Transition(IEvent e) ChannelGroups = receiveReconnectSuccess.ChannelGroups, Cursor = receiveReconnectSuccess.Cursor, - }.With(new EmitStatusInvocation(receiveReconnectSuccess.Status)), + }.With( + new EmitMessagesInvocation(receiveReconnectSuccess.Cursor, receiveReconnectSuccess.Messages) + ), Events.ReceiveReconnectFailureEvent receiveReconnectFailure => new ReceiveReconnectingState() { @@ -70,7 +69,7 @@ public override TransitionResult Transition(IEvent e) ChannelGroups = this.ChannelGroups, Cursor = this.Cursor, AttemptedRetries = this.AttemptedRetries + 1 - }.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 f142c356d..c9a8d0385 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceiveStoppedState.cs @@ -1,27 +1,22 @@ -using PubnubApi.EventEngine.Core; +using System.Linq; +using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; namespace PubnubApi.EventEngine.Subscribe.States { public class ReceiveStoppedState : SubscriptionState { - public SubscriptionCursor Cursor; - public override TransitionResult Transition(IEvent e) { return e switch { - Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() - { - - }, + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { }, 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, - }, Events.ReconnectEvent reconnect => new HandshakingState() @@ -29,7 +24,6 @@ public override TransitionResult Transition(IEvent e) Channels = reconnect.Channels, ChannelGroups = reconnect.ChannelGroups, Cursor = reconnect.Cursor, - }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceiveStoppedState() @@ -37,7 +31,6 @@ public override TransitionResult Transition(IEvent e) Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor, - }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs index 6f3635d5a..843bb65b9 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/ReceivingState.cs @@ -2,13 +2,12 @@ using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Invocations; using PubnubApi.EventEngine.Subscribe.Common; +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(); @@ -18,10 +17,7 @@ public override TransitionResult Transition(IEvent e) { return e switch { - Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() - { - - }, + Events.UnsubscribeAllEvent unsubscribeAll => new UnsubscribedState() { }, Events.ReceiveSuccessEvent receiveSuccess => new ReceivingState() { @@ -30,16 +26,14 @@ public override TransitionResult Transition(IEvent e) Cursor = receiveSuccess.Cursor, }.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, }, Events.SubscriptionRestoredEvent subscriptionRestored => new ReceivingState() @@ -47,7 +41,6 @@ public override TransitionResult Transition(IEvent e) Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, Cursor = subscriptionRestored.Cursor, - }, Events.DisconnectEvent disconnect => new ReceiveStoppedState() @@ -62,7 +55,7 @@ public override TransitionResult Transition(IEvent e) { Channels = this.Channels, ChannelGroups = this.ChannelGroups, - Cursor = this.Cursor, + Cursor = receiveFailure.Cursor, AttemptedRetries = 0 }, diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs index 88262594d..db46f3ad5 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/States/UnsubscribedState.cs @@ -1,4 +1,5 @@ -using PubnubApi.EventEngine.Core; +using System.Linq; +using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.Common; namespace PubnubApi.EventEngine.Subscribe.States @@ -11,15 +12,16 @@ 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, }, Events.SubscriptionRestoredEvent subscriptionRestored => new States.ReceivingState() { Channels = subscriptionRestored.Channels, ChannelGroups = subscriptionRestored.ChannelGroups, - Cursor = subscriptionRestored.Cursor + Cursor = subscriptionRestored.Cursor, }, _ => null diff --git a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs index 968d781ae..aee80cf2e 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngine.cs @@ -1,13 +1,26 @@ using PubnubApi.EndPoint; using PubnubApi.EventEngine.Core; using PubnubApi.EventEngine.Subscribe.States; +using System.Collections.Generic; +using System.Linq; +using PubnubApi.EventEngine.Subscribe.Common; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; +using PubnubApi.EventEngine.Subscribe.Events; 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); + public string[] Channels { get; set; } = new string[] {}; public string[] Channelgroups { get; set; } = new string[] {}; @@ -19,19 +32,23 @@ internal SubscribeEventEngine(Pubnub pubnubInstance, Action> messageListener = null) { this.subscribeManager = subscribeManager; + var handshakeHandler = new Effects.HandshakeEffectHandler(subscribeManager, EventQueue); + var handshakeReconnectHandler = new Effects.HandshakeReconnectEffectHandler(pubnubConfiguration, EventQueue, handshakeHandler); - // initialize the handler, pass dependencies - var handshakeHandler = new Effects.HandshakeEffectHandler(pubnubConfiguration,subscribeManager, eventQueue); 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(pubnubConfiguration, EventQueue, receiveHandler); - var receiveHandler = new Effects.ReceivingEffectHandler(pubnubConfiguration,subscribeManager, eventQueue); 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,5 +56,42 @@ internal SubscribeEventEngine(Pubnub pubnubInstance, currentState = new UnsubscribedState(); } + 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) + { + 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 c729d2049..ae2a5ad20 100644 --- a/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs +++ b/src/Api/PubnubApi/EventEngine/Subscribe/SubscribeEventEngineFactory.cs @@ -6,36 +6,37 @@ 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); - if (engineinstances.TryAdd(instanceId, subscribeEventEngine)) { + var subscribeEventEngine = new SubscribeEventEngine(pubnubInstance, pubnubConfiguration: pubnubConfiguration, subscribeManager,statusListener, messageListener); + if (engineInstances.TryAdd(instanceId, subscribeEventEngine)) { return subscribeEventEngine; - } else { + } + else { throw new Exception("Subscribe event engine initialisation failed!"); } - + } } } diff --git a/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs b/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs index fd5de104e..31239f59a 100644 --- a/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs +++ b/src/Api/PubnubApi/Interface/IPubnubUnitTest.cs @@ -1,9 +1,5 @@ -using PubnubApi.PubnubEventEngine; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; namespace PubnubApi { diff --git a/src/Api/PubnubApi/Interface/IUnsubscribeOperation.cs b/src/Api/PubnubApi/Interface/IUnsubscribeOperation.cs new file mode 100644 index 000000000..9c784bef8 --- /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/Model/Consumer/PNStatus.cs b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs index aceb340e7..2c0d34eb3 100644 --- a/src/Api/PubnubApi/Model/Consumer/PNStatus.cs +++ b/src/Api/PubnubApi/Model/Consumer/PNStatus.cs @@ -16,12 +16,16 @@ public PNStatus() { } public PNStatus(Exception e, PNOperationType operationType, PNStatusCategory category, IEnumerable affectedChannels = null, IEnumerable affectedChannelGroups = null) { - this.Error = true; + this.Error = e != null; this.Operation = operationType; this.ErrorData = new PNErrorData(e?.Message, e); this.AffectedChannels = affectedChannels?.ToList(); this.AffectedChannelGroups = affectedChannelGroups?.ToList(); this.Category = category; + if (!Error) + { + this.StatusCode = 200; + } } internal PNStatus(object endpointOperation) diff --git a/src/Api/PubnubApi/Pubnub.cs b/src/Api/PubnubApi/Pubnub.cs index e792d3ead..48565e88a 100644 --- a/src/Api/PubnubApi/Pubnub.cs +++ b/src/Api/PubnubApi/Pubnub.cs @@ -2,9 +2,12 @@ using System.Collections.Generic; using System.Globalization; using System.Reflection; -using PubnubApi.EventEngine.Subscribe; using PubnubApi.EndPoint; +using PubnubApi.EventEngine.Subscribe; using PubnubApi.EventEngine.Subscribe.Events; +using PubnubApi.EventEngine.Subscribe.Common; +using PubnubApi.Interface; +using PubnubApi.EventEngine.Presence; #if !NET35 && !NET40 using System.Collections.Concurrent; #endif @@ -22,6 +25,7 @@ public class Pubnub private object savedSubscribeOperation; private readonly string savedSdkVerion; private SubscribeEventEngineFactory subscribeEventEngineFactory; + private PresenceEventEngineFactory presenceEventengineFactory; private List subscribeCallbackListenerList { get; @@ -48,35 +52,48 @@ public ISubscribeOperation Subscribe() if (pubnubConfig[InstanceId].EnableEventEngine) { if (pubnubConfig[InstanceId].PresenceInterval > 0) { - presenceOperation = new PresenceOperation(this, InstanceId, pubnubLog, telemetryManager, tokenManager, new EventEngine.Presence.PresenceEventEngineFactory()); + presenceOperation = new PresenceOperation(this, InstanceId, pubnubLog, telemetryManager, tokenManager, presenceEventengineFactory); } - EndPoint.SubscribeOperation2 subscribeOperation = new EndPoint.SubscribeOperation2(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, new SubscribeEventEngineFactory(), InstanceId, this, presenceOperation); + EndPoint.SubscribeEndpoint subscribeOperation = new EndPoint.SubscribeEndpoint(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this.subscribeEventEngineFactory, presenceOperation, InstanceId ,this); subscribeOperation.SubscribeListenerList = subscribeCallbackListenerList; - subscribeEventEngineFactory = subscribeOperation.subscribeEventEngineFactory; - // subscribeOperation.CurrentPubnubInstance(this); savedSubscribeOperation = subscribeOperation; return subscribeOperation; } else { EndPoint.SubscribeOperation subscribeOperation = new EndPoint.SubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, null, tokenManager, this); - //subscribeOperation.CurrentPubnubInstance(this); savedSubscribeOperation = subscribeOperation; return subscribeOperation; } } - public EndPoint.UnsubscribeOperation Unsubscribe() + public IUnsubscribeOperation Unsubscribe() { - EndPoint.UnsubscribeOperation unsubscribeOperation = new EndPoint.UnsubscribeOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, this, subscribeEventEngineFactory); - 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, presenceEventengineFactory, 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() { - EndPoint.UnsubscribeAllOperation unSubscribeAllOperation = new EndPoint.UnsubscribeAllOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, this); - return unSubscribeAllOperation; + if (pubnubConfig[InstanceId].EnableEventEngine) + { + EndPoint.UnsubscribeAllEndpoint unsubscribeAllEndpoint = new EndPoint.UnsubscribeAllEndpoint(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, subscribeEventEngineFactory, presenceEventengineFactory, this); + return unsubscribeAllEndpoint; + } + else + { + EndPoint.UnsubscribeAllOperation unSubscribeAllOperation = new EndPoint.UnsubscribeAllOperation(pubnubConfig.ContainsKey(InstanceId) ? pubnubConfig[InstanceId] : null, JsonPluggableLibrary, pubnubUnitTest, pubnubLog, telemetryManager, tokenManager, this); + return unSubscribeAllOperation; + } } public EndPoint.PublishOperation Publish() @@ -324,7 +341,7 @@ public EndPoint.GetMessageActionsOperation GetMessageActions() #endregion -#region "PubNub API Channel Group Methods" + #region "PubNub API Channel Group Methods" public EndPoint.AddChannelsToChannelGroupOperation AddChannelsToChannelGroup() { @@ -543,26 +560,71 @@ public void SetAuthToken(string token) public bool Reconnect() { bool ret = false; - if (savedSubscribeOperation is EndPoint.SubscribeOperation) - { - EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; - if (subscibeOperationInstance != null) - { - ret = subscibeOperationInstance.Retry(true, false); - } - } - return ret; + if (pubnubConfig[InstanceId].EnableEventEngine) { + if (subscribeEventEngineFactory.HasEventEngine(InstanceId)) { + 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 + }); + } + if (presenceEventengineFactory.HasEventEngine(InstanceId)) { + var presenceEventEngine = presenceEventengineFactory.GetEventEngine(InstanceId); + + presenceEventEngine.EventQueue.Enqueue(new EventEngine.Presence.Events.ReconnectEvent() { + Input = new EventEngine.Presence.Common.PresenceInput() { + Channels = (presenceEventEngine.CurrentState as EventEngine.Presence.States.APresenceState).Input.Channels, + ChannelGroups = (presenceEventEngine.CurrentState as EventEngine.Presence.States.APresenceState).Input.ChannelGroups + } + }); + } + } else { + if (savedSubscribeOperation is EndPoint.SubscribeOperation) { + EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; + if (subscibeOperationInstance != null) { + ret = subscibeOperationInstance.Retry(true, false); + } + } + } + return ret; } 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 + }); + } + if (presenceEventengineFactory.HasEventEngine(InstanceId)) { + var presenceEventEngine = presenceEventengineFactory.GetEventEngine(InstanceId); + + presenceEventEngine.EventQueue.Enqueue(new EventEngine.Presence.Events.ReconnectEvent() { + Input = new EventEngine.Presence.Common.PresenceInput() { + Channels = (presenceEventEngine.CurrentState as EventEngine.Presence.States.APresenceState).Input.Channels, + ChannelGroups = (presenceEventEngine.CurrentState as EventEngine.Presence.States.APresenceState).Input.ChannelGroups + } + }); + } + } + else + { + if (savedSubscribeOperation is EndPoint.SubscribeOperation) + { + EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; + if (subscibeOperationInstance != null) + { + ret = subscibeOperationInstance.Retry(true, resetSubscribeTimetoken); + } } } return ret; @@ -571,12 +633,28 @@ 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 }); + } + if (presenceEventengineFactory.HasEventEngine(InstanceId)) { + var presenceEventEngine = presenceEventengineFactory.GetEventEngine(InstanceId); + + presenceEventEngine.EventQueue.Enqueue(new EventEngine.Presence.Events.DisconnectEvent()); + } + } + else + { + if (savedSubscribeOperation is EndPoint.SubscribeOperation) + { + EndPoint.SubscribeOperation subscibeOperationInstance = savedSubscribeOperation as EndPoint.SubscribeOperation; + if (subscibeOperationInstance != null) + { + ret = subscibeOperationInstance.Retry(false); + } } } return ret; @@ -895,6 +973,8 @@ public Pubnub(PNConfiguration config) { savedSdkVerion = Version; InstanceId = Guid.NewGuid().ToString(); + subscribeEventEngineFactory = new SubscribeEventEngineFactory(); + presenceEventengineFactory = new PresenceEventEngineFactory(); pubnubConfig.AddOrUpdate(InstanceId, config, (k, o) => config); if (config != null) diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index 710f16c77..bbc87b84b 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -70,16 +70,13 @@ - - 0.3.1 + None - - 1.0.2856 + None - - 1.0.2856 + None @@ -89,8 +86,7 @@ - - 0.3.1 + None @@ -106,38 +102,33 @@ None - - 4.3.0 + None - - 4.3.0 + None - - 4.3.0 + None - - 4.3.0 + None - - 4.3.0 + None diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 6a4c3feb3..50294fc96 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -155,6 +155,7 @@ + EndPoint\PubSub\SubscribeManager.cs @@ -162,10 +163,13 @@ EndPoint\PubSub\SubscribeOperation.cs - EndPoint\PubSub\UnsubscribeAllOperation.cs + + EndPoint\PubSub\UnsubscribeAllEndpoint.cs + + EndPoint\PubSub\UnsubscribeOperation.cs @@ -228,16 +232,6 @@ - - - - - - - - - - @@ -263,10 +257,8 @@ - - @@ -302,6 +294,7 @@ Interface\IPubnubUnitTest.cs + Interface\IUrlRequestBuilder.cs diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 63d168975..8c92b14f7 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -268,6 +268,7 @@ + EndPoint\PubSub\SubscribeManager.cs @@ -277,12 +278,13 @@ EndPoint\PubSub\SubscribeOperation.cs - - EndPoint\PubSub\SubscribeOperation2.cs - EndPoint\PubSub\UnsubscribeAllOperation.cs + + EndPoint\PubSub\UnsubscribeAllEndpoint.cs + + EndPoint\PubSub\UnsubscribeOperation.cs @@ -345,23 +347,14 @@ - - - - - - - - - - - - + + + @@ -380,10 +373,8 @@ - - @@ -415,6 +406,7 @@ Interface\ISubscribeOperation.cs + Interface\IPubnubLog.cs @@ -768,11 +760,12 @@ + + - diff --git a/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs index 0e453a53f..e169e283c 100644 --- a/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs +++ b/src/UnitTests/AcceptanceTests/Steps/EventEngineSteps.cs @@ -11,7 +11,6 @@ using System.Text.Json; using System.Threading.Channels; using System.Threading; -using PubnubApi.PubnubEventEngine; using TechTalk.SpecFlow.Assist; using System.Net.Http;