public IComponent Export()
        {
            using (_logger.Scope("Exporting active entities to component."))
            {
                var rootComponent = _factory.Create();

                // Apply the active entities to the component tree.
                var activeEvents = _table
                                   .GetActiveEntities <EventEntity>()
                                   .ToList()
                                   .Where(e =>
                                          _table
                                          .GetChildEntities <MessageEntity, EventEntity>(e)
                                          .ToList()
                                          .Any())
                                   .ToList();

                _logger.LogInformation("Found {EventCount} active events with messages.", activeEvents.Count);

                var activeIncidentGroups = activeEvents
                                           .SelectMany(e =>
                                                       _table
                                                       .GetChildEntities <IncidentGroupEntity, EventEntity>(e)
                                                       .Where(i => i.IsActive)
                                                       .ToList())
                                           .ToList();

                _logger.LogInformation("Found {GroupCount} active incident groups linked to active events with messages.", activeIncidentGroups.Count);

                var activeEntities = activeIncidentGroups
                                     .Concat <IComponentAffectingEntity>(activeEvents)
                                     // Only apply entities with a non-Up status.
                                     .Where(e => e.AffectedComponentStatus != (int)ComponentStatus.Up)
                                     // If multiple events are affecting a single region, the event with the highest severity should affect the component.
                                     .GroupBy(e => e.AffectedComponentPath)
                                     .Select(g => g.OrderByDescending(e => e.AffectedComponentStatus).First())
                                     .ToList();

                _logger.LogInformation("Active entities affect {PathCount} distinct subcomponents.", activeEntities.Count);
                foreach (var activeEntity in activeEntities)
                {
                    using (_logger.Scope("Applying active entity affecting {AffectedComponentPath} of severity {AffectedComponentStatus} at {StartTime} to root component",
                                         activeEntity.AffectedComponentPath, (ComponentStatus)activeEntity.AffectedComponentStatus, activeEntity.StartTime))
                    {
                        var currentComponent = rootComponent.GetByPath(activeEntity.AffectedComponentPath);

                        if (currentComponent == null)
                        {
                            throw new InvalidOperationException($"Couldn't find component with path {activeEntity.AffectedComponentPath} corresponding to active entities.");
                        }

                        currentComponent.Status = (ComponentStatus)activeEntity.AffectedComponentStatus;
                    }
                }

                return(rootComponent);
            }
        }
        public async Task <bool> CanBeAggregatedByAsync(ParsedIncident input, TAggregationEntity aggregationEntity)
        {
            using (_logger.Scope("Determining if entity can be linked to aggregation {AggregationRowKey}", aggregationEntity.RowKey))
            {
                if (!_table.GetChildEntities <TChildEntity, TAggregationEntity>(aggregationEntity).ToList().Any())
                {
                    // A manually created aggregation will have no children. We cannot use an aggregation that was manually created.
                    // It is also possible that some bug or data issue has broken this aggregation. If that is the case, we cannot use it either.
                    _logger.LogInformation("Cannot link entity to aggregation because it is not linked to any children.");
                    return(false);
                }

                // To guarantee that the aggregation reflects the latest information and is actually active, we must update it.
                await _aggregationUpdater.UpdateAsync(aggregationEntity, input.StartTime);

                if (!aggregationEntity.IsActive && input.IsActive)
                {
                    _logger.LogInformation("Cannot link entity to aggregation because it has been deactivated and the incident has not been.");
                    return(false);
                }

                _logger.LogInformation("Entity can be linked to aggregation.");
                return(true);
            }
        }
Esempio n. 3
0
        public Event Export(EventEntity eventEntity)
        {
            using (_logger.Scope("Exporting event {EventRowKey}.", eventEntity.RowKey))
            {
                var messages = _table.GetChildEntities <MessageEntity, EventEntity>(eventEntity)
                               .ToList()
                               // Don't show empty messages.
                               .Where(m => !string.IsNullOrEmpty(m.Contents))
                               .ToList();

                _logger.LogInformation("Event has {MessageCount} messages that are not empty.", messages.Count);

                if (!messages.Any())
                {
                    return(null);
                }

                return(new Event(
                           eventEntity.AffectedComponentPath,
                           eventEntity.StartTime,
                           eventEntity.EndTime,
                           messages
                           .OrderBy(m => m.Time)
                           .Select(m => new Message(m.Time, m.Contents))));
            }
        }
        public bool CanPostMessages(IncidentGroupEntity group, DateTime cursor)
        {
            var duration = (group.EndTime ?? cursor) - group.StartTime;

            if (duration < _eventStartMessageDelay)
            {
                _logger.LogInformation("Incident group has not been active for longer than the messaging delay.");
                return(false);
            }

            var linkedIncidentsQuery = _table.GetChildEntities <IncidentEntity, IncidentGroupEntity>(group);

            var activeIncidents = linkedIncidentsQuery
                                  .Where(i => i.IsActive)
                                  .ToList();

            var incidentsActiveAfterDelay = linkedIncidentsQuery
                                            .Where(i => i.EndTime >= group.StartTime + _eventStartMessageDelay)
                                            .ToList();

            _logger.LogInformation("Incident group is linked to {ActiveIncidentsCount} active incidents and {DelayActiveIncidentsCount} incidents that were active after the messaging delay.",
                                   activeIncidents.Count, incidentsActiveAfterDelay.Count);

            var hasBeenActiveLongerThanDelay = activeIncidents.Any() || incidentsActiveAfterDelay.Any();

            return(hasBeenActiveLongerThanDelay);
        }
        public IEnumerable <MessageChangeEvent> Get(EventEntity eventEntity, DateTime cursor)
        {
            var linkedGroups = _table.GetChildEntities <IncidentGroupEntity, EventEntity>(eventEntity).ToList();
            var events       = new List <MessageChangeEvent>();

            _logger.LogInformation("Event has {IncidentGroupsCount} linked incident groups.", linkedGroups.Count);
            foreach (var linkedGroup in linkedGroups)
            {
                using (_logger.Scope("Getting status changes from incident group {IncidentGroupRowKey}.", linkedGroup.RowKey))
                {
                    if (!_filter.CanPostMessages(linkedGroup, cursor))
                    {
                        _logger.LogInformation("Incident group did not pass filter. Cannot post messages about it.");
                        continue;
                    }

                    var path      = linkedGroup.AffectedComponentPath;
                    var status    = (ComponentStatus)linkedGroup.AffectedComponentStatus;
                    var startTime = linkedGroup.StartTime;
                    _logger.LogInformation("Incident group started at {StartTime}.", startTime);
                    events.Add(new MessageChangeEvent(startTime, path, status, MessageType.Start));
                    if (!linkedGroup.IsActive)
                    {
                        var endTime = linkedGroup.EndTime.Value;
                        _logger.LogInformation("Incident group ended at {EndTime}.", endTime);
                        events.Add(new MessageChangeEvent(endTime, path, status, MessageType.End));
                    }
                }
            }

            return(events);
        }
Esempio n. 6
0
        public async Task UpdateAsync(TAggregationEntity aggregationEntity, DateTime cursor)
        {
            aggregationEntity = aggregationEntity ?? throw new ArgumentNullException(nameof(aggregationEntity));

            using (_logger.Scope("Updating aggregation {AggregationRowKey} given cursor {Cursor}.", aggregationEntity.RowKey, cursor))
            {
                if (!aggregationEntity.IsActive)
                {
                    _logger.LogInformation("Aggregation is inactive, cannot update.");
                    return;
                }

                var hasActiveOrRecentChildren = false;
                var children = _table
                               .GetChildEntities <TChildEntity, TAggregationEntity>(aggregationEntity)
                               .ToList();

                if (children.Any())
                {
                    _logger.LogInformation("Aggregation has {ChildrenCount} children. Updating each child.", children.Count);
                    foreach (var child in children)
                    {
                        await _aggregatedEntityUpdater.UpdateAsync(child, cursor);

                        hasActiveOrRecentChildren =
                            hasActiveOrRecentChildren ||
                            child.IsActive ||
                            child.EndTime > cursor - _groupEndDelay;
                    }
                }
                else
                {
                    _logger.LogInformation("Aggregation has no children and must have been created manually, cannot update.");
                    return;
                }

                if (!hasActiveOrRecentChildren)
                {
                    _logger.LogInformation("Deactivating aggregation because its children are inactive and too old.");
                    var lastEndTime = children.Max(i => i.EndTime.Value);
                    aggregationEntity.EndTime = lastEndTime;

                    await _table.ReplaceAsync(aggregationEntity);
                }
                else
                {
                    _logger.LogInformation("Aggregation has active or recent children so it will not be deactivated.");
                }
            }
        }