public async Task <ActionResponseBag> ParseStreamAsync(Stream stream)
        {
            string            text    = null;
            ActionResponseBag actions = new ActionResponseBag();

            try
            {
                using (StreamReader streamReader = new StreamReader(stream))
                {
                    text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
                }
            }
            catch
            {
                return(actions);
            }
            if (!string.IsNullOrEmpty(text))
            {
                try
                {
                    actions = JsonConvert.DeserializeObject <ActionResponseBag>(text);
                }
                catch (Exception ex)
                {
                    throw new TargetedNotificationsException(ex.Message, ex);
                }
            }
            return(actions);
        }
 public override Task <GroupedRemoteSettings> Start()
 {
     Interlocked.Exchange(ref startTask, Task.Run(async delegate
     {
         GroupedRemoteSettings result = null;
         queryIteration++;
         ActionResponseBag actionResponseBag = await GetTargetedNotificationActionsAsync().ConfigureAwait(false);
         if (actionResponseBag != null)
         {
             ProcessActionResponseBag(actionResponseBag);
             result = await ProcessRemoteSettingsFromTargetedNotificationsAsync().ConfigureAwait(false);
         }
         StartAgainAfter(serviceQueryLoopTimeSpan);
         return(result);
     }));
     return(startTask);
 }
Example #3
0
        protected override async Task <ActionResponseBag> GetTargetedNotificationActionsAsync()
        {
            IEnumerable <Task <IEnumerable <ActionResponse> > > tasks = directories.SelectMany((IDirectoryReader d) => d.ReadAllFiles()).Select(async delegate(DirectoryReaderContext x)
            {
                try
                {
                    return(await localTestParser.ParseStreamAsync(x));
                }
                catch (TargetedNotificationsException exception)
                {
                    logger.LogError("Error parsing test file: " + x.DirectoryName + "-" + x.FileName, exception);
                }
                return(Enumerable.Empty <ActionResponse>());
            });
            ActionResponseBag actionResponseBag  = new ActionResponseBag();
            ActionResponseBag actionResponseBag2 = actionResponseBag;

            actionResponseBag2.Actions = (await Task.WhenAll(tasks)).SelectMany((IEnumerable <ActionResponse> x) => x);
            return(actionResponseBag);
        }
        protected override async Task <ActionResponseBag> GetTargetedNotificationActionsAsync()
        {
            IEnumerable <string> previouslyCachedRuleIds = useCache ? notificationAndCourtesyCache.GetAllCachedRuleIds(cacheTimeoutMs) : Enumerable.Empty <string>();
            Stream stream = await SendTargetedNotificationsRequestAsync(previouslyCachedRuleIds).ConfigureAwait(false);

            if (stream != null)
            {
                try
                {
                    ActionResponseBag actionResponseBag = await notificationsParser.ParseStreamAsync(stream).ConfigureAwait(false);

                    if (useCache)
                    {
                        notificationAndCourtesyCache.MergeNewResponse(actionResponseBag, previouslyCachedRuleIds, cacheTimeoutMs);
                    }
                    return(actionResponseBag);
                }
                catch (TargetedNotificationsException exception)
                {
                    string eventName = "VS/Core/TargetedNotifications/ApiResponseParseFailure";
                    Dictionary <string, object> additionalProperties = new Dictionary <string, object>
                    {
                        {
                            "VS.Core.TargetedNotifications.ApiResponseMs",
                            apiTimer?.ElapsedMilliseconds
                        },
                        {
                            "VS.Core.TargetedNotifications.Iteration",
                            queryIteration
                        }
                    };
                    targetedNotificationsTelemetry.PostCriticalFault(eventName, "Failed to parse TN API response", exception, additionalProperties);
                }
            }
            return(null);
        }
        private void ProcessActionResponseBag(ActionResponseBag response)
        {
            logger.LogInfo($"Received {response.Actions.Count()} actions from {Name}");
            List <string> list;

            try
            {
                actionsAndCategoriesLock.Wait();
                list = tnActions.Values.SelectMany((Dictionary <string, ActionResponse> x) => x.Keys).ToList();
                foreach (ActionResponse action in response.Actions)
                {
                    if (!tnActions.TryGetValue(action.ActionPath, out Dictionary <string, ActionResponse> value))
                    {
                        value = new Dictionary <string, ActionResponse>(StringComparer.OrdinalIgnoreCase);
                        tnActions[action.ActionPath] = value;
                    }
                    if (action.RuleId != null)
                    {
                        value[action.RuleId] = action;
                    }
                    else
                    {
                        logger.LogInfo("Skipping action that has no RuleId " + action.ActionPath + " from " + Name);
                    }
                }
                foreach (ActionCategory category in response.Categories)
                {
                    ActionCategories[category.CategoryId] = category;
                }
            }
            finally
            {
                actionsAndCategoriesLock.Release();
            }
            lock (subscriptionLockObject)
            {
                try
                {
                    foreach (string key in tnSubscriptionCallbacks.Keys)
                    {
                        foreach (Type key2 in tnSubscriptionCallbacks[key].Keys)
                        {
                            MethodInfo methodInfo = typeof(TargetedNotificationsProviderBase).GetMethod("SubscribeActionsInternal", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(key2);
                            foreach (object item in tnSubscriptionCallbacks[key][key2])
                            {
                                methodInfo.Invoke(this, new object[3]
                                {
                                    key,
                                    item,
                                    list
                                });
                            }
                        }
                    }
                }
                catch (Exception exception)
                {
                    string eventName = "VS/Core/TargetedNotifications/ReSubscribeFailure";
                    targetedNotificationsTelemetry.PostCriticalFault(eventName, "failure", exception);
                }
            }
            string eventName2 = "VS/Core/TargetedNotifications/ActionsReceived";
            IEnumerable <ActionResponse> source            = response.Actions ?? Enumerable.Empty <ActionResponse>();
            Dictionary <string, IEnumerable <string> > val = (from x in source
                                                              group x by x.ActionPath).ToDictionary((IGrouping <string, ActionResponse> x) => x.Key, (IGrouping <string, ActionResponse> x) => x.Select((ActionResponse y) => y.RuleId));
            List <string> list2 = ActionCategories.Keys.ToList();
            Dictionary <string, object> additionalProperties = new Dictionary <string, object>
            {
                {
                    "VS.Core.TargetedNotifications.ProviderName",
                    Name
                },
                {
                    "VS.Core.TargetedNotifications.Actions",
                    new TelemetryComplexProperty(val)
                },
                {
                    "VS.Core.TargetedNotifications.ActionCount",
                    source.Count()
                },
                {
                    "VS.Core.TargetedNotifications.Categories",
                    new TelemetryComplexProperty(list2)
                },
                {
                    "VS.Core.TargetedNotifications.CategoryCount",
                    list2.Count
                },
                {
                    "VS.Core.TargetedNotifications.ApiResponseMs",
                    apiTimer?.ElapsedMilliseconds
                },
                {
                    "VS.Core.TargetedNotifications.Iteration",
                    queryIteration
                }
            };

            targetedNotificationsTelemetry.PostSuccessfulOperation(eventName2, additionalProperties);
        }
 /// <summary>
 /// Called when a new ActionResponseBag is received from the
 /// service. This merges that new data into the existing cache
 /// </summary>
 /// <param name="newResponse">An ActionResponseBag received from the Azure API</param>
 /// <param name="previouslyCachedRuleIds">Set of rule IDs that were previously read from the cache and should not be written back</param>
 /// <param name="timeoutMs">Maximum time to wait, in milliseconds, for the cache lock. Leave null for infinite.</param>
 public void MergeNewResponse(ActionResponseBag newResponse, IEnumerable <string> previouslyCachedRuleIds, int?timeoutMs = null)
 {
     if (Storage.Lock(timeoutMs))
     {
         Stopwatch stopwatch = Stopwatch.StartNew();
         try
         {
             DateTime utcNow = DateTime.UtcNow;
             bool     flag   = false;
             CachedTargetedNotifications localCacheCopy = Storage.GetLocalCacheCopy();
             List <string> list  = new List <string>();
             List <string> list2 = new List <string>();
             foreach (ActionResponse action in newResponse.Actions)
             {
                 if (action.SendAlways)
                 {
                     if (localCacheCopy.Actions.Remove(action.RuleId))
                     {
                         flag = true;
                     }
                 }
                 else if (!previouslyCachedRuleIds.Contains(action.RuleId))
                 {
                     localCacheCopy.Actions[action.RuleId] = new CachedActionResponseTime
                     {
                         CachedTime      = (localCacheCopy.Actions.ContainsKey(action.RuleId) ? localCacheCopy.Actions[action.RuleId].CachedTime : utcNow),
                         MaxWaitTimeSpan = ((action.MaxWaitTimeSpan == null) ? defaultMaxWaitTimeSpan : TimeSpan.Parse(action.MaxWaitTimeSpan))
                     };
                     list.Add(action.RuleId);
                     flag = true;
                 }
             }
             string[] array = localCacheCopy.Actions.Keys.ToArray();
             foreach (string text in array)
             {
                 CachedActionResponseTime cachedActionResponseTime = localCacheCopy.Actions[text];
                 if (utcNow >= cachedActionResponseTime.CachedTime.Add(cachedActionResponseTime.MaxWaitTimeSpan))
                 {
                     localCacheCopy.Actions.Remove(text);
                     list2.Add(text);
                     flag = true;
                 }
             }
             foreach (ActionCategory category in newResponse.Categories)
             {
                 if (localCacheCopy.Categories.ContainsKey(category.CategoryId))
                 {
                     TimeSpan timeSpan = TimeSpan.Parse(category.WaitTimeSpan);
                     if (localCacheCopy.Categories[category.CategoryId].WaitTimeSpan != timeSpan)
                     {
                         localCacheCopy.Categories[category.CategoryId].WaitTimeSpan = timeSpan;
                         flag = true;
                     }
                 }
             }
             array = localCacheCopy.Categories.Keys.ToArray();
             foreach (string key in array)
             {
                 CachedActionCategoryTime cachedActionCategoryTime = localCacheCopy.Categories[key];
                 if (utcNow >= cachedActionCategoryTime.LastSent.Add(cachedActionCategoryTime.WaitTimeSpan))
                 {
                     localCacheCopy.Categories.Remove(key);
                     flag = true;
                 }
             }
             if (flag)
             {
                 Storage.SetLocalCache(localCacheCopy);
             }
             if (list.Count > 0)
             {
                 responseUsesCachedRules = true;
             }
             stopwatch.Stop();
             if (list.Count > 0 || list2.Count > 0 || localCacheCopy.Actions.Count > 0 || localCacheCopy.Categories.Count > 0)
             {
                 string eventName = "VS/Core/TargetedNotifications/CacheMerged";
                 Dictionary <string, object> additionalProperties = new Dictionary <string, object>
                 {
                     {
                         "VS.Core.TargetedNotifications.AddedActions",
                         new TelemetryComplexProperty(list)
                     },
                     {
                         "VS.Core.TargetedNotifications.AddedActionsCount",
                         list.Count
                     },
                     {
                         "VS.Core.TargetedNotifications.ExpiredActions",
                         new TelemetryComplexProperty(list2)
                     },
                     {
                         "VS.Core.TargetedNotifications.ExpiredActionsCount",
                         list2.Count
                     },
                     {
                         "VS.Core.TargetedNotifications.CachedActions",
                         new TelemetryComplexProperty(localCacheCopy.Actions.Keys.ToList())
                     },
                     {
                         "VS.Core.TargetedNotifications.CachedActionsCount",
                         localCacheCopy.Actions.Count
                     },
                     {
                         "VS.Core.TargetedNotifications.CachedCategories",
                         new TelemetryComplexProperty(localCacheCopy.Categories.Keys.ToList())
                     },
                     {
                         "VS.Core.TargetedNotifications.CachedCategoriesCount",
                         localCacheCopy.Categories.Count
                     },
                     {
                         "VS.Core.TargetedNotifications.DurationMs",
                         stopwatch.ElapsedMilliseconds
                     }
                 };
                 telemetry.PostSuccessfulOperation(eventName, additionalProperties);
             }
         }
         catch (Exception exception)
         {
             stopwatch.Stop();
             string   eventName2 = "VS/Core/TargetedNotifications/CacheMergeFailure";
             string[] array2     = (from a in newResponse.Actions
                                    where !a.SendAlways
                                    select a.RuleId).Except(previouslyCachedRuleIds).ToArray();
             Dictionary <string, object> additionalProperties2 = new Dictionary <string, object>
             {
                 {
                     "VS.Core.TargetedNotifications.SendOnceActions",
                     new TelemetryComplexProperty(array2)
                 },
                 {
                     "VS.Core.TargetedNotifications.SendOnceActionsCount",
                     array2.Length
                 },
                 {
                     "VS.Core.TargetedNotifications.DurationMs",
                     stopwatch.ElapsedMilliseconds
                 }
             };
             telemetry.PostCriticalFault(eventName2, "Failed to merge new response with cache", exception, additionalProperties2);
         }
         finally
         {
             Storage.Unlock();
         }
     }
     else
     {
         string eventName3 = "VS/Core/TargetedNotifications/CacheLockTimeout";
         Dictionary <string, object> additionalProperties3 = new Dictionary <string, object>
         {
             {
                 "VS.Core.TargetedNotifications.Operation",
                 "MergeNewResponse"
             },
             {
                 "VS.Core.TargetedNotifications.TimeoutMs",
                 timeoutMs
             }
         };
         telemetry.PostDiagnosticFault(eventName3, "Timeout acquiring cache lock", null, additionalProperties3);
     }
 }