/// <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() }); }
/// <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 }); }
/// <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}"); } }