Beispiel #1
0
        /// <summary>
        /// Extract all alert properties from the specified object
        /// </summary>
        /// <param name="propertiesOwner">The object from which to extract the properties</param>
        /// <param name="order">The order to use</param>
        /// <param name="parentPropertyName">The parent property name</param>
        /// <param name="propertiesToIgnore">
        /// A list of property names to ignore. Properties with matching names will not be included in the result.
        /// </param>
        /// <returns>The extracted properties</returns>
        private static List <AlertProperty> ExtractProperties(object propertiesOwner, Order order, string parentPropertyName, HashSet <string> propertiesToIgnore)
        {
            if (order == null)
            {
                throw new ArgumentNullException(nameof(order));
            }

            // The null object has no properties
            if (propertiesOwner == null)
            {
                return(new List <AlertProperty>());
            }

            // Collect all object properties, and sort them by order
            var orderedProperties = propertiesOwner.GetType().GetProperties().Select(property =>
            {
                // Get the property value
                object propertyValue = property.GetValue(propertiesOwner);

                // Skip the property if it is empty
                if (propertyValue == null ||
                    (propertyValue is string stringValue && string.IsNullOrWhiteSpace(stringValue)) ||
                    (propertyValue is ICollection collectionValue && collectionValue.Count == 0))
                {
                    return(null);
                }

                // Get the presentation attribute
                AlertPresentationPropertyAttribute presentationAttribute = property.GetCustomAttribute <AlertPresentationPropertyAttribute>();

                // Return the presentation attribute, property, and value
                return(new { PresentationAttribute = presentationAttribute, Property = property, Value = propertyValue });
            })
                                    .Where(x => x != null)
                                    .OrderBy(p => p.PresentationAttribute?.Order ?? -1)
                                    .ThenBy(p => p.Property.Name);

            // Process the properties, in order
            List <AlertProperty> alertProperties = new List <AlertProperty>();

            foreach (var p in orderedProperties)
            {
                if (p.PresentationAttribute != null)
                {
                    alertProperties.AddRange(CreateAlertProperties(propertiesOwner, p.Property, p.PresentationAttribute, p.Value, order, parentPropertyName));
                }
                else if (!propertiesToIgnore.Contains(p.Property.Name))
                {
                    // Get the raw alert property - a property with no presentation
                    alertProperties.Add(new RawAlertProperty(CombinePropertyNames(parentPropertyName, p.Property.Name), p.Value));
                }
            }

            return(alertProperties);
        }
        /// <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
            });
        }
Beispiel #3
0
        /// <summary>
        /// Creates one or more <see cref="AlertProperty"/> objects based on an alert presentation property
        /// </summary>
        /// <param name="propertyOwner">The object that has the property</param>
        /// <param name="property">The property info of the property to create</param>
        /// <param name="presentationAttribute">The attribute defining the presentation of the alert property</param>
        /// <param name="propertyValue">The property value</param>
        /// <param name="order">The order to use</param>
        /// <param name="parentPropertyName">The parent property name</param>
        /// <returns>The <see cref="AlertProperty"/> objects</returns>
        private static IEnumerable <AlertProperty> CreateAlertProperties(object propertyOwner, PropertyInfo property, AlertPresentationPropertyAttribute presentationAttribute, object propertyValue, Order order, string parentPropertyName)
        {
            // Get the attribute display name
            string displayName = presentationAttribute.DisplayName.EvaluateInterpolatedString(propertyOwner);

            // Get the property name (and add the parent property name as prefix, if provided)
            string propertyName = string.IsNullOrWhiteSpace(presentationAttribute.PropertyName) ? property.Name : presentationAttribute.PropertyName;

            propertyName = CombinePropertyNames(parentPropertyName, propertyName);

            // Try converting the property value to PropertyReference
            var propertyReferenceValue = propertyValue as PropertyReference;

            // Return the presentation property according to the property type
            switch (presentationAttribute)
            {
            case ChartPropertyAttribute chartAttribute:
                yield return(CreateChartAlertProperty(propertyValue, propertyName, displayName, chartAttribute, order));

                break;

            case MetricChartPropertyAttribute _:
                if (!(propertyValue is MetricChart metricChart))
                {
                    throw new ArgumentException($"A {nameof(MetricChartPropertyAttribute)} can only be applied to properties of type {nameof(MetricChart)}");
                }

                yield return(CreateMetricChartAlertProperty(propertyName, displayName, order, metricChart));

                break;

            case LongTextPropertyAttribute longTextPropertyAttribute:
                if (propertyReferenceValue != null)
                {
                    if (!string.IsNullOrEmpty(longTextPropertyAttribute.FormatString))
                    {
                        throw new ArgumentException($"A {nameof(LongTextPropertyAttribute)} applied to properties of type {nameof(PropertyReference)} cannot have format string");
                    }

                    yield return(new LongTextReferenceAlertProperty(propertyName, displayName, order.Next(), propertyReferenceValue.ReferencePath, propertyReferenceValue.IsOptional, propertyReferenceValue.IsPropertySerialized));
                }
                else
                {
                    yield return(new LongTextAlertProperty(propertyName, displayName, order.Next(), PropertyValueToString(propertyOwner, property, propertyValue, longTextPropertyAttribute.FormatString)));
                }

                break;

            case TextPropertyAttribute textPropertyAttribute:
                if (propertyReferenceValue != null)
                {
                    if (!string.IsNullOrEmpty(textPropertyAttribute.FormatString))
                    {
                        throw new ArgumentException($"A {nameof(TextPropertyAttribute)} applied to properties of type {nameof(PropertyReference)} cannot have format string");
                    }

                    yield return(new TextReferenceAlertProperty(propertyName, displayName, order.Next(), propertyReferenceValue.ReferencePath, propertyReferenceValue.IsOptional, propertyReferenceValue.IsPropertySerialized));
                }
                else
                {
                    yield return(new TextAlertProperty(propertyName, displayName, order.Next(), PropertyValueToString(propertyOwner, property, propertyValue, textPropertyAttribute.FormatString)));
                }

                break;

            case ListPropertyAttribute _:
                if (!(propertyValue is IList list))
                {
                    throw new ArgumentException($"A {nameof(ListPropertyAttribute)} can only be applied to properties of type IList");
                }

                foreach (AlertProperty p in CreateAlertPropertiesFromList(propertyName, list, order))
                {
                    yield return(p);
                }

                break;

            case KeyValuePropertyAttribute keyValueAttribute:
                yield return(CreateKeyValueAlertProperty(propertyOwner, propertyValue, propertyName, displayName, keyValueAttribute, order));

                break;

            case TablePropertyAttribute tableAttribute:
                yield return(CreateTableAlertProperty(propertyValue, propertyName, displayName, tableAttribute, order));

                break;

            case AzureResourceManagerRequestPropertyAttribute armRequestAttribute:
                if (!(propertyValue is AzureResourceManagerRequest armRequest))
                {
                    throw new ArgumentException($"A {nameof(AzureResourceManagerRequestPropertyAttribute)} can only be applied to properties of type {nameof(AzureResourceManagerRequest)}");
                }

                List <AlertProperty> properties = armRequest.ExtractProperties(AzureResourceManagerRequestBaseClassPropertiesNames);
                if (properties.Any(prop => !(prop is IReferenceAlertProperty || prop is RawAlertProperty)))
                {
                    // We support raw properties because they can contain values used in interpolated display name string
                    throw new ArgumentException($"An {nameof(AzureResourceManagerRequest)} can only have reference and raw alert properties");
                }

                List <AlertProperty> propertiesToDisplay = properties.Where(prop => prop is IReferenceAlertProperty).ToList();

                yield return(new AzureResourceManagerRequestAlertProperty(propertyName, order.Next(), armRequest.RequestUri, propertiesToDisplay.Cast <DisplayableAlertProperty>().ToList(), armRequest.IsOptional));

                break;

            default:
                throw new InvalidEnumArgumentException($"Unable to handle presentation attribute of type {presentationAttribute.GetType().Name}");
            }
        }
Beispiel #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
        }