Exemple #1
0
        /// <summary>
        /// Creates a presentation from an alert
        /// </summary>
        /// <param name="alert">The alert</param>
        /// <param name="request">The Smart Detector request</param>
        /// <param name="smartDetectorName">The Smart Detector name</param>
        /// <param name="usedLogAnalysisClient">Indicates whether a log analysis client was used to create the alert</param>
        /// <param name="usedMetricClient">Indicates whether a metric client was used to create the alert</param>
        /// <returns>The presentation</returns>
        public static ContractsAlert CreateContractsAlert(this Alert alert, SmartDetectorAnalysisRequest request, string smartDetectorName, bool usedLogAnalysisClient, bool usedMetricClient)
        {
            // A null alert has null presentation
            if (alert == null)
            {
                return(null);
            }

            // Create presentation elements for each alert property
            List <AlertProperty> alertProperties = alert.ExtractProperties(AlertBaseClassPropertiesNames);

            // Generate the alert's correlation hash based on its predicates
            string correlationHash = string.Join("##", alert.ExtractPredicates().OrderBy(x => x.Key).Select(x => x.Key + "|" + x.Value.ToString())).ToSha256Hash();

            // Get the alert's signal type based on the clients used to create the alert
            SignalType signalType = GetSignalType(usedLogAnalysisClient, usedMetricClient);

            // Return the presentation object
            return(new ContractsAlert
            {
                Title = alert.Title,
                OccurenceTime = alert.OccurenceTime,
                ResourceId = alert.ResourceIdentifier.ToResourceId(),
                CorrelationHash = correlationHash,
                SmartDetectorId = request.SmartDetectorId,
                SmartDetectorName = smartDetectorName,
                AnalysisTimestamp = DateTime.UtcNow,
                AnalysisWindowSizeInMinutes = (int)request.Cadence.TotalMinutes,
                AlertProperties = alertProperties,
                SignalType = signalType,
                ResolutionParameters = alert.AlertResolutionParameters?.CreateContractsResolutionParameters()
            });
        }
Exemple #2
0
        /// <summary>
        /// Extracts the predicate properties from the alert. Predicate properties are properties
        /// in the alert's object which are marked by <see cref="PredicatePropertyAttribute"/>.
        /// </summary>
        /// <param name="alert">The alert to extract the predicates from.</param>
        /// <returns>A dictionary mapping each predicate property name to its value.</returns>
        public static Dictionary <string, object> ExtractPredicates(this Alert alert)
        {
            if (alert == null)
            {
                throw new ArgumentNullException(nameof(alert));
            }

            var predicates = new Dictionary <string, object>();

            foreach (PropertyInfo property in alert.GetType().GetProperties().Where(prop => prop.GetCustomAttribute <PredicatePropertyAttribute>() != null))
            {
                predicates[property.Name] = property.GetValue(alert);
            }

            return(predicates);
        }
        /// <summary>
        /// Creates an <see cref="AlertPropertyLegacy"/> based on an alert presentation V1 property
        /// </summary>
        /// <param name="alert">The alert</param>
        /// <param name="presentationAttribute">The attribute defining the presentation V1 of the alert property</param>
        /// <param name="queryRunInfo">The query run information</param>
        /// <param name="propertyStringValue">The property string value</param>
        /// <returns>An <see cref="AlertPropertyLegacy"/></returns>
#pragma warning disable CS0612 // Type or member is obsolete; Task to remove obsolete code #1312924
        private static AlertPropertyLegacy CreateAlertPropertyLegacy(Alert alert, AlertPresentationPropertyAttribute presentationAttribute, QueryRunInfo queryRunInfo, string propertyStringValue)
        {
            // Verify that if the entity is a chart or query, then query run information was provided
            if (queryRunInfo == null && (presentationAttribute.Section == AlertPresentationSection.Chart || presentationAttribute.Section == AlertPresentationSection.AdditionalQuery))
            {
                throw new InvalidAlertPresentationException($"The presentation contains an item for the {presentationAttribute.Section} section, but no telemetry data client was provided");
            }

            // Get the attribute title and information balloon - support interpolated strings
            string attributeTitle       = StringExtensions.EvaluateInterpolatedString(presentationAttribute.Title, alert);
            string attributeInfoBalloon = StringExtensions.EvaluateInterpolatedString(presentationAttribute.InfoBalloon, alert);

            // Add the presentation property
            return(new AlertPropertyLegacy()
            {
                Name = attributeTitle,
                Value = propertyStringValue,
                DisplayCategory = GetDisplayCategoryFromPresentationSection(presentationAttribute.Section),
                InfoBalloon = attributeInfoBalloon,
                Order = presentationAttribute.Order
            });
        }
Exemple #4
0
        /// <summary>
        /// Creates a presentation from a alert
        /// </summary>
        /// <param name="alert">The alert</param>
        /// <param name="request">The Smart Detector request</param>
        /// <param name="smartDetectorName">The Smart Detector name</param>
        /// <param name="queryRunInfo">The query run information</param>
        /// <returns>The presentation</returns>
        public static ContractsAlert CreateContractsAlert(this Alert alert, SmartDetectorExecutionRequest request, string smartDetectorName, QueryRunInfo queryRunInfo)
        {
            // A null alert has null presentation
            if (alert == null)
            {
                return(null);
            }

            // Create presentation elements for each alert property
            Dictionary <string, string> predicates      = new Dictionary <string, string>();
            List <AlertProperty>        alertProperties = new List <AlertProperty>();
            Dictionary <string, string> rawProperties   = new Dictionary <string, string>();

            foreach (PropertyInfo property in alert.GetType().GetProperties())
            {
                // Get the property value
                string propertyValue = PropertyValueToString(property.GetValue(alert));
                if (string.IsNullOrWhiteSpace(propertyValue))
                {
                    // not accepting empty properties
                    continue;
                }

                rawProperties[property.Name] = propertyValue;

                // Check if this property is a predicate
                if (property.GetCustomAttribute <AlertPredicatePropertyAttribute>() != null)
                {
                    predicates[property.Name] = propertyValue;
                }

                // Get the presentation attribute
                AlertPresentationPropertyAttribute attribute = property.GetCustomAttribute <AlertPresentationPropertyAttribute>();
                if (attribute != null)
                {
                    // Verify that if the entity is a chart or query, then query run information was provided
                    if (queryRunInfo == null && (attribute.Section == AlertPresentationSection.Chart || attribute.Section == AlertPresentationSection.AdditionalQuery))
                    {
                        throw new InvalidAlertPresentationException($"The presentation contains an item for the {attribute.Section} section, but no telemetry data client was provided");
                    }

                    // Get the attribute title and information balloon - support interpolated strings
                    string attributeTitle       = Smart.Format(attribute.Title, alert);
                    string attributeInfoBalloon = Smart.Format(attribute.InfoBalloon, alert);

                    // Add the presentation property
                    alertProperties.Add(new AlertProperty
                    {
                        Name            = attributeTitle,
                        Value           = propertyValue,
                        DisplayCategory = GetDisplayCategoryFromPresentationSection(attribute.Section),
                        InfoBalloon     = attributeInfoBalloon
                    });
                }
            }

            string id              = string.Join("##", alert.GetType().FullName, JsonConvert.SerializeObject(request), JsonConvert.SerializeObject(alert)).Hash();
            string resourceId      = alert.ResourceIdentifier.ToResourceId();
            string correlationHash = string.Join("##", predicates.OrderBy(x => x.Key).Select(x => x.Key + "|" + x.Value)).Hash();

            // Return the presentation object
            return(new ContractsAlert
            {
                Id = id,
                Title = alert.Title,
                ResourceId = resourceId,
                CorrelationHash = correlationHash,
                SmartDetectorId = request.SmartDetectorId,
                SmartDetectorName = smartDetectorName,
                AnalysisTimestamp = DateTime.UtcNow,
                AnalysisWindowSizeInMinutes = (int)request.Cadence.TotalMinutes,
                Properties = alertProperties,
                RawProperties = rawProperties,
                QueryRunInfo = queryRunInfo
            });
        }
        /// <summary>
        /// Creates a presentation from an alert
        /// </summary>
        /// <param name="alert">The alert</param>
        /// <param name="request">The Smart Detector request</param>
        /// <param name="smartDetectorName">The Smart Detector name</param>
        /// <param name="queryRunInfo">The query run information</param>
        /// <param name="usedLogAnalysisClient">Indicates whether a log analysis client was used to create the alert</param>
        /// <param name="usedMetricClient">Indicates whether a metric client was used to create the alert</param>
        /// <returns>The presentation</returns>
        public static ContractsAlert CreateContractsAlert(this Alert alert, SmartDetectorExecutionRequest request, string smartDetectorName, QueryRunInfo queryRunInfo, bool usedLogAnalysisClient, bool usedMetricClient)
        {
            // A null alert has null presentation
            if (alert == null)
            {
                return(null);
            }

            // Create presentation elements for each alert property
            Dictionary <string, string> predicates = new Dictionary <string, string>();

            #pragma warning disable CS0612 // Type or member is obsolete; Task to remove obsolete code #1312924
            List <AlertPropertyLegacy> alertPropertiesLegacy = new List <AlertPropertyLegacy>();
            #pragma warning restore CS0612 // Type or member is obsolete; Task to remove obsolete code #1312924
            List <AlertProperty>        alertProperties = new List <AlertProperty>();
            Dictionary <string, string> rawProperties   = new Dictionary <string, string>();
            List <string> alertBaseClassPropertiesNames = typeof(Alert).GetProperties().Select(p => p.Name).ToList();
            foreach (PropertyInfo property in alert.GetType().GetProperties())
            {
                // Get the property value
                object propertyValue       = property.GetValue(alert);
                string propertyStringValue = PropertyValueToString(propertyValue);
                if (string.IsNullOrWhiteSpace(propertyStringValue) || (propertyValue is ICollection value && value.Count == 0))
                {
                    // not accepting empty properties
                    continue;
                }

                rawProperties[property.Name] = propertyStringValue;

                // Check if this property is a predicate
                if (property.GetCustomAttribute <AlertPredicatePropertyAttribute>() != null)
                {
                    predicates[property.Name] = propertyStringValue;
                }

                // Get the v1 presentation attribute
                AlertPresentationPropertyAttribute presentationAttribute = property.GetCustomAttribute <AlertPresentationPropertyAttribute>();
                if (presentationAttribute != null)
                {
                    alertPropertiesLegacy.Add(CreateAlertPropertyLegacy(alert, presentationAttribute, queryRunInfo, propertyStringValue));
                }

                // Get the v2 presentation attribute
                AlertPresentationPropertyV2Attribute presentationV2Attribute = property.GetCustomAttribute <AlertPresentationPropertyV2Attribute>();
                if (presentationV2Attribute != null)
                {
                    alertProperties.Add(CreateAlertProperty(alert, presentationV2Attribute, property.Name, propertyValue));
                }
                else if (!alertBaseClassPropertiesNames.Contains(property.Name))
                {
                    // Get the raw alert property - a property with no presentation
                    alertProperties.Add(new RawAlertProperty(property.Name, propertyValue));
                }
            }

            string id              = string.Join("##", alert.GetType().FullName, JsonConvert.SerializeObject(request), JsonConvert.SerializeObject(alert)).Hash();
            string resourceId      = alert.ResourceIdentifier.ToResourceId();
            string correlationHash = string.Join("##", predicates.OrderBy(x => x.Key).Select(x => x.Key + "|" + x.Value)).Hash();

            // Get the alert's signal type based on the clients used to create the alert
            SignalType signalType = GetSignalType(usedLogAnalysisClient, usedMetricClient);

            // Return the presentation object
            #pragma warning disable CS0612 // Type or member is obsolete; Task to remove obsolete code #1312924
            return(new ContractsAlert
            {
                Id = id,
                State = (alert.State == AlertState.Active) ? ContractsAlertState.Active : ContractsAlertState.Resolved,
                Title = alert.Title,
                ResourceId = resourceId,
                CorrelationHash = correlationHash,
                SmartDetectorId = request.SmartDetectorId,
                SmartDetectorName = smartDetectorName,
                AnalysisTimestamp = DateTime.UtcNow,
                AnalysisWindowSizeInMinutes = (int)request.Cadence.TotalMinutes,
                Properties = alertPropertiesLegacy,
                AlertProperties = alertProperties,
                RawProperties = rawProperties,
                QueryRunInfo = queryRunInfo,
                SignalType = signalType
            });

            #pragma warning restore CS0612 // Type or member is obsolete; Task to remove obsolete code #1312924
        }
        #pragma warning restore CS0612 // Type or member is obsolete; Task to remove obsolete code #1312924

        /// <summary>
        /// Creates an <see cref="AlertProperty"/> based on an alert presentation V2 property
        /// </summary>
        /// <param name="alert">The alert</param>
        /// <param name="presentationAttribute">The attribute defining the presentation V2 of the alert property</param>
        /// <param name="propertyDefaultName">The property default name</param>
        /// <param name="propertyValue">The property value</param>
        /// <returns>An <see cref="AlertProperty"/></returns>
        private static AlertProperty CreateAlertProperty(Alert alert, AlertPresentationPropertyV2Attribute presentationAttribute, string propertyDefaultName, object propertyValue)
        {
            // Get the attribute display name
            string displayName = StringExtensions.EvaluateInterpolatedString(presentationAttribute.DisplayName, alert);

            // Get the property name
            string propertyName = string.IsNullOrWhiteSpace(presentationAttribute.PropertyName) ? propertyDefaultName : presentationAttribute.PropertyName;

            // Return the presentation property according to the property type
            switch (presentationAttribute)
            {
            case AlertPresentationChartAttribute chartAttribute:
                if (!(propertyValue is IList <ChartPoint> listValues))
                {
                    throw new ArgumentException("An AlertPresentationChartAttribute can only be applied to properties of type IList<ChartPoint>");
                }

                return(new ChartAlertProperty(
                           propertyName,
                           displayName,
                           presentationAttribute.Order,
                           ConvertChartTypeToContractsChartType(chartAttribute.ChartType),
                           ConvertChartAxisTypeToContractsChartType(chartAttribute.XAxisType),
                           ConvertChartAxisTypeToContractsChartType(chartAttribute.YAxisType),
                           listValues.Select(point => new ContractsChartPoint(point.X, point.Y)).ToList()));

            case AlertPresentationLongTextAttribute longTextAttribute:
                return(new LongTextAlertProprety(propertyName, displayName, presentationAttribute.Order, PropertyValueToString(propertyValue)));

            case AlertPresentationTextAttribute textAttribute:
                return(new TextAlertProperty(propertyName, displayName, presentationAttribute.Order, PropertyValueToString(propertyValue)));

            case AlertPresentationUrlAttribute urlAttribute:
                if (!(propertyValue is Uri uriValue))
                {
                    throw new ArgumentException("An AlertPresentationUrlAttribute can only be applied to properties of type Uri");
                }

                if (!uriValue.IsAbsoluteUri)
                {
                    throw new ArgumentException("The URI supplied must be absolute");
                }

                string linkText = StringExtensions.EvaluateInterpolatedString(urlAttribute.LinkText, alert);
                return(new TextAlertProperty(propertyName, displayName, presentationAttribute.Order, $"<a href=\"{uriValue.ToString()}\">{linkText}</a>"));

            case AlertPresentationKeyValueAttribute keyValueAttribute:
                if (!(propertyValue is IDictionary <string, string> keyValuePropertyValue))
                {
                    throw new ArgumentException("An AlertPresentationKeyValueAttribute can only be applied to properties of type IDictionary<string, string>");
                }

                if (keyValueAttribute.ShowHeaders)
                {
                    string keyHeaderName   = StringExtensions.EvaluateInterpolatedString(keyValueAttribute.KeyHeaderName, alert);
                    string valueHeaderName = StringExtensions.EvaluateInterpolatedString(keyValueAttribute.ValueHeaderName, alert);
                    return(new KeyValueAlertProperty(propertyName, displayName, presentationAttribute.Order, keyHeaderName, valueHeaderName, keyValuePropertyValue));
                }
                else
                {
                    return(new KeyValueAlertProperty(propertyName, displayName, presentationAttribute.Order, keyValuePropertyValue));
                }

            case AlertPresentationSingleColumnTableAttribute singleColumnTableAttribute:
                if (!(propertyValue is IList singleColumnTablePropertyValue))
                {
                    throw new ArgumentException("An AlertPresentationSingleColumnTableAttribute can only be applied to properties of type IList");
                }

                return(new TableAlertProperty(propertyName, displayName, presentationAttribute.Order, singleColumnTableAttribute.ShowHeaders, singleColumnTablePropertyValue));

            case AlertPresentationTableAttribute tableAttribute:
                if (!(propertyValue is IList tablePropertyValue))
                {
                    throw new ArgumentException("An AlertPresentationTableAttribute can only be applied to properties of type IList");
                }

                Type tableRowType = GetGenericListType(propertyValue.GetType());
                if (tableRowType == null)
                {
                    throw new ArgumentException("An AlertPresentationTableAttribute can only be applied to properties of type IList<>");
                }

                return(new TableAlertProperty(propertyName, displayName, presentationAttribute.Order, tableAttribute.ShowHeaders, CreateTableColumnsFromRowType(tableRowType), tablePropertyValue));

            default:
                throw new InvalidEnumArgumentException($"Unable to handle presentation attribute of type {presentationAttribute.GetType().Name}");
            }
        }