public void MarkAsRegressedTest()
        {
            RemoveData();
            _repository.Add(StackData.GenerateStack(id: TestConstants.StackId, projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId, dateFixed: DateTime.Now.SubtractMonths(1)));
            _client.Refresh();

            var stack = _repository.GetById(TestConstants.StackId);

            Assert.NotNull(stack);
            Assert.False(stack.IsRegressed);
            Assert.NotNull(stack.DateFixed);

            _repository.MarkAsRegressed(TestConstants.StackId);

            _client.Refresh();
            stack = _repository.GetById(TestConstants.StackId);
            Assert.NotNull(stack);
            Assert.True(stack.IsRegressed);
            Assert.Null(stack.DateFixed);
        }
        private object ProcessNotification(IMessage <EventNotification> message)
        {
            int emailsSent         = 0;
            EventNotification data = message.GetBody();

            Log.Trace().Message("Process notification: project={0} event={1} stack={2}", data.Event.ProjectId, data.Event.Id, data.Event.StackId).Write();

            var project = _projectRepository.GetByIdCached(data.Event.ProjectId);

            if (project == null)
            {
                Log.Error().Message("Could not load project {0}.", data.Event.ProjectId).Write();
                return(null);
            }
            Log.Trace().Message("Loaded project: name={0}", project.Name).Write();

            var organization = _organizationRepository.GetByIdCached(project.OrganizationId);

            if (organization == null)
            {
                Log.Error().Message("Could not load organization {0}.", project.OrganizationId).Write();
                return(null);
            }
            Log.Trace().Message("Loaded organization: name={0}", organization.Name).Write();

            var stack = _stackRepository.GetById(data.Event.StackId);

            if (stack == null)
            {
                Log.Error().Message("Could not load stack {0}.", data.Event.StackId).Write();
                return(null);
            }

            if (!organization.HasPremiumFeatures)
            {
                Log.Trace().Message("Skipping because organization does not have premium features.").Write();
                return(null);
            }

            if (stack.DisableNotifications || stack.IsHidden)
            {
                Log.Trace().Message("Skipping because stack notifications are disabled or it's hidden.").Write();
                return(null);
            }

            Log.Trace().Message("Loaded stack: title={0}", stack.Title).Write();
            int totalOccurrences = stack.TotalOccurrences;

            // after the first 5 occurrences, don't send a notification for the same stack more then once every 15 minutes
            var lastTimeSent = _cacheClient.Get <DateTime>(String.Concat("NOTIFICATION_THROTTLE_", data.Event.StackId));

            if (totalOccurrences > 5 && !data.IsRegression && lastTimeSent != DateTime.MinValue &&
                lastTimeSent > DateTime.Now.AddMinutes(-15))
            {
                Log.Info().Message("Skipping message because of throttling: last sent={0} occurrences={1}", lastTimeSent, totalOccurrences).Write();
                return(null);
            }

            foreach (var kv in project.NotificationSettings)
            {
                var settings = kv.Value;
                Log.Trace().Message("Processing notification: user={0}", kv.Key).Write();

                var user = _userRepository.GetById(kv.Key);
                if (user == null || String.IsNullOrEmpty(user.EmailAddress))
                {
                    Log.Error().Message("Could not load user {0} or blank email address {1}.", kv.Key, user != null ? user.EmailAddress : "").Write();
                    continue;
                }

                if (!user.IsEmailAddressVerified)
                {
                    Log.Info().Message("User {0} with email address {1} has not been verified.", kv.Key, user != null ? user.EmailAddress : "").Write();
                    continue;
                }

                if (!user.EmailNotificationsEnabled)
                {
                    Log.Trace().Message("User {0} with email address {1} has email notifications disabled.", kv.Key, user != null ? user.EmailAddress : "").Write();
                    continue;
                }

                if (!user.OrganizationIds.Contains(project.OrganizationId))
                {
                    // TODO: Should this notification setting be deleted?
                    Log.Error().Message("Unauthorized user: project={0} user={1} organization={2} event={3}", project.Id, kv.Key,
                                        project.OrganizationId, data.Event.Id).Write();
                    continue;
                }

                Log.Trace().Message("Loaded user: email={0}", user.EmailAddress).Write();

                bool shouldReportOccurrence    = settings.Mode != NotificationMode.None;
                bool shouldReportCriticalError = settings.ReportCriticalErrors && data.IsCritical;
                bool shouldReportRegression    = settings.ReportRegressions && data.IsRegression;

                Log.Trace().Message("Settings: mode={0} critical={1} regression={2} 404={3} bots={4}",
                                    settings.Mode, settings.ReportCriticalErrors,
                                    settings.ReportRegressions, settings.Report404Errors,
                                    settings.ReportKnownBotErrors).Write();
                Log.Trace().Message("Should process: occurrence={0} critical={1} regression={2}",
                                    shouldReportOccurrence, shouldReportCriticalError,
                                    shouldReportRegression).Write();

                if (settings.Mode == NotificationMode.New && !data.IsNew)
                {
                    shouldReportOccurrence = false;
                    Log.Trace().Message("Skipping because message is not new.").Write();
                }

                // check for 404s if the user has elected to not report them
                if (shouldReportOccurrence && settings.Report404Errors == false && data.Event.IsNotFound)
                {
                    shouldReportOccurrence = false;
                    Log.Trace().Message("Skipping because message is 404.").Write();
                }

                var requestInfo = data.Event.GetRequestInfo();
                // check for known bots if the user has elected to not report them
                if (shouldReportOccurrence && settings.ReportKnownBotErrors == false &&
                    requestInfo != null && !String.IsNullOrEmpty(requestInfo.UserAgent))
                {
                    ClientInfo info = null;
                    try {
                        info = Parser.GetDefault().Parse(requestInfo.UserAgent);
                    } catch (Exception ex) {
                        Log.Warn().Project(data.Event.ProjectId).Message("Unable to parse user agent {0}. Exception: {1}",
                                                                         requestInfo.UserAgent, ex.Message).Write();
                    }

                    if (info != null && info.Device.IsSpider)
                    {
                        shouldReportOccurrence = false;
                        Log.Trace().Message("Skipping because message is bot.").Write();
                    }
                }

                // stack being set to send all will override all other settings
                if (!shouldReportOccurrence && !shouldReportCriticalError && !shouldReportRegression)
                {
                    continue;
                }

                // don't send notifications in non-production mode to email addresses that are not on the outbound email list.
                if (Settings.Current.WebsiteMode != WebsiteMode.Production &&
                    !Settings.Current.AllowedOutboundAddresses.Contains(v => user.EmailAddress.ToLowerInvariant().Contains(v)))
                {
                    Log.Trace().Message("Skipping because email is not on the outbound list and not in production mode.").Write();
                    continue;
                }

                Log.Trace().Message("Sending email to {0}...", user.EmailAddress).Write();
                _mailer.SendNotice(user.EmailAddress, data);
                emailsSent++;
                Log.Trace().Message("Done sending email.").Write();
            }

            // if we sent any emails, mark the last time a notification for this stack was sent.
            if (emailsSent > 0)
            {
                _cacheClient.Set(String.Concat("NOTIFICATION_THROTTLE_", data.Event.StackId), DateTime.Now, DateTime.Now.AddMinutes(15));
            }

            return(null);
        }
        public override void Process(EventContext ctx)
        {
            if (String.IsNullOrEmpty(ctx.Event.StackId))
            {
                if (_stackRepository == null)
                {
                    throw new InvalidOperationException("You must pass a non-null stackRepository parameter to the constructor.");
                }

                // only add default signature info if no other signature info has been added
                if (ctx.StackSignatureData.Count == 0)
                {
                    ctx.StackSignatureData.Add("Type", ctx.Event.Type);
                    if (!String.IsNullOrEmpty(ctx.Event.Source))
                    {
                        ctx.StackSignatureData.Add("Source", ctx.Event.Source);
                    }
                }

                string signatureHash = ctx.StackSignatureData.Values.Any(v => v != null) ? ctx.StackSignatureData.Values.ToSHA1() : null;
                ctx.SetProperty("__SignatureHash", signatureHash);

                ctx.Stack = _stackRepository.GetStackBySignatureHash(ctx.Event.ProjectId, signatureHash);
                if (ctx.Stack == null)
                {
                    Log.Trace().Message("Creating new error stack.").Write();
                    ctx.IsNew = true;
                    ctx.Event.IsFirstOccurrence = true;

                    string title = _formattingPluginManager.GetStackTitle(ctx.Event);
                    var    stack = new Stack {
                        OrganizationId                                      = ctx.Event.OrganizationId,
                        ProjectId                                           = ctx.Event.ProjectId,
                        SignatureInfo                                       = new SettingsDictionary(ctx.StackSignatureData),
                        SignatureHash                                       = signatureHash,
                        Title                                               = title != null?title.Truncate(1000) : null,
                                                           Tags             = ctx.Event.Tags ?? new TagSet(),
                                                           Type             = ctx.Event.Type,
                                                           TotalOccurrences = 1,
                                                           FirstOccurrence  = ctx.Event.Date.UtcDateTime,
                                                           LastOccurrence   = ctx.Event.Date.UtcDateTime
                    };

                    ctx.Stack = _stackRepository.Add(stack, true);
                }

                Log.Trace().Message("Updating error's ErrorStackId to: {0}", ctx.Stack.Id).Write();
                ctx.Event.StackId = ctx.Stack.Id;
            }
            else
            {
                ctx.Stack = _stackRepository.GetById(ctx.Event.StackId, true);

                if (ctx.Stack == null || ctx.Stack.ProjectId != ctx.Event.ProjectId)
                {
                    throw new ApplicationException("Invalid StackId.");
                }

                ctx.SetProperty("__SignatureHash", ctx.Stack.SignatureHash);
            }

            if (!ctx.IsNew && ctx.Event.Tags != null && ctx.Event.Tags.Count > 0)
            {
                if (ctx.Stack.Tags == null)
                {
                    ctx.Stack.Tags = new TagSet();
                }

                List <string> newTags = ctx.Event.Tags.Where(t => !ctx.Stack.Tags.Contains(t)).ToList();
                if (newTags.Count > 0)
                {
                    ctx.Stack.Tags.AddRange(newTags);
                    _stackRepository.Save(ctx.Stack, true);
                }
            }

            // sync the fixed and hidden flags to the error occurrence
            ctx.Event.IsFixed  = ctx.Stack.DateFixed.HasValue;
            ctx.Event.IsHidden = ctx.Stack.IsHidden;
        }
        protected async override Task <JobResult> RunInternalAsync(CancellationToken token)
        {
            QueueEntry <EventNotification> queueEntry = null;

            try {
                queueEntry = _queue.Dequeue();
            } catch (Exception ex) {
                if (!(ex is TimeoutException))
                {
                    return(JobResult.FromException(ex, "An error occurred while trying to dequeue the next EventNotification: {0}", ex.Message));
                }
            }
            if (queueEntry == null)
            {
                return(JobResult.Success);
            }

            var  eventNotification = queueEntry.Value;
            bool shouldLog         = eventNotification.Event.ProjectId != Settings.Current.InternalProjectId;
            int  emailsSent        = 0;

            Log.Trace().Message("Process notification: project={0} event={1} stack={2}", eventNotification.Event.ProjectId, eventNotification.Event.Id, eventNotification.Event.StackId).WriteIf(shouldLog);

            var project = _projectRepository.GetById(eventNotification.Event.ProjectId, true);

            if (project == null)
            {
                queueEntry.Abandon();
                return(JobResult.FailedWithMessage("Could not load project {0}.", eventNotification.Event.ProjectId));
            }
            Log.Trace().Message("Loaded project: name={0}", project.Name).WriteIf(shouldLog);

            var organization = _organizationRepository.GetById(project.OrganizationId, true);

            if (organization == null)
            {
                queueEntry.Abandon();
                return(JobResult.FailedWithMessage("Could not load organization {0}.", project.OrganizationId));
            }
            Log.Trace().Message("Loaded organization: name={0}", organization.Name).WriteIf(shouldLog);

            var stack = _stackRepository.GetById(eventNotification.Event.StackId);

            if (stack == null)
            {
                queueEntry.Abandon();
                return(JobResult.FailedWithMessage("Could not load stack {0}.", eventNotification.Event.StackId));
            }

            if (!organization.HasPremiumFeatures)
            {
                queueEntry.Complete();
                Log.Info().Message("Skipping \"{0}\" because organization \"{1}\" does not have premium features.", eventNotification.Event.Id, eventNotification.Event.OrganizationId).WriteIf(shouldLog);
                return(JobResult.Success);
            }

            if (stack.DisableNotifications || stack.IsHidden)
            {
                queueEntry.Complete();
                Log.Info().Message("Skipping \"{0}\" because stack \"{1}\" notifications are disabled or stack is hidden.", eventNotification.Event.Id, eventNotification.Event.StackId).WriteIf(shouldLog);
                return(JobResult.Success);
            }

            if (token.IsCancellationRequested)
            {
                queueEntry.Abandon();
                return(JobResult.Cancelled);
            }

            Log.Trace().Message("Loaded stack: title={0}", stack.Title).WriteIf(shouldLog);
            int totalOccurrences = stack.TotalOccurrences;

            // after the first 2 occurrences, don't send a notification for the same stack more then once every 30 minutes
            var lastTimeSent = _cacheClient.Get <DateTime>(String.Concat("notify:stack-throttle:", eventNotification.Event.StackId));

            if (totalOccurrences > 2 &&
                !eventNotification.IsRegression &&
                lastTimeSent != DateTime.MinValue &&
                lastTimeSent > DateTime.Now.AddMinutes(-30))
            {
                queueEntry.Complete();
                Log.Info().Message("Skipping message because of stack throttling: last sent={0} occurrences={1}", lastTimeSent, totalOccurrences).WriteIf(shouldLog);
                return(JobResult.Success);
            }

            // don't send more than 10 notifications for a given project every 30 minutes
            var    projectTimeWindow = TimeSpan.FromMinutes(30);
            string cacheKey          = String.Concat("notify:project-throttle:", eventNotification.Event.ProjectId, "-", DateTime.UtcNow.Floor(projectTimeWindow).Ticks);
            long   notificationCount = _cacheClient.Increment(cacheKey, 1, projectTimeWindow);

            if (notificationCount > 10 && !eventNotification.IsRegression)
            {
                queueEntry.Complete();
                Log.Info().Project(eventNotification.Event.ProjectId).Message("Skipping message because of project throttling: count={0}", notificationCount).WriteIf(shouldLog);
                return(JobResult.Success);
            }

            if (token.IsCancellationRequested)
            {
                queueEntry.Abandon();
                return(JobResult.Cancelled);
            }

            foreach (var kv in project.NotificationSettings)
            {
                var settings = kv.Value;
                Log.Trace().Message("Processing notification: user={0}", kv.Key).WriteIf(shouldLog);

                var user = _userRepository.GetById(kv.Key);
                if (user == null || String.IsNullOrEmpty(user.EmailAddress))
                {
                    Log.Error().Message("Could not load user {0} or blank email address {1}.", kv.Key, user != null ? user.EmailAddress : "").Write();
                    continue;
                }

                if (!user.IsEmailAddressVerified)
                {
                    Log.Info().Message("User {0} with email address {1} has not been verified.", kv.Key, user != null ? user.EmailAddress : "").WriteIf(shouldLog);
                    continue;
                }

                if (!user.EmailNotificationsEnabled)
                {
                    Log.Info().Message("User {0} with email address {1} has email notifications disabled.", kv.Key, user != null ? user.EmailAddress : "").WriteIf(shouldLog);
                    continue;
                }

                if (!user.OrganizationIds.Contains(project.OrganizationId))
                {
                    Log.Error().Message("Unauthorized user: project={0} user={1} organization={2} event={3}", project.Id, kv.Key, project.OrganizationId, eventNotification.Event.Id).Write();
                    continue;
                }

                Log.Trace().Message("Loaded user: email={0}", user.EmailAddress).WriteIf(shouldLog);

                bool shouldReportNewError      = settings.ReportNewErrors && eventNotification.IsNew && eventNotification.Event.IsError();;
                bool shouldReportCriticalError = settings.ReportCriticalErrors && eventNotification.IsCritical && eventNotification.Event.IsError();;
                bool shouldReportRegression    = settings.ReportEventRegressions && eventNotification.IsRegression;
                bool shouldReportNewEvent      = settings.ReportNewEvents && eventNotification.IsNew;
                bool shouldReportCriticalEvent = settings.ReportCriticalEvents && eventNotification.IsCritical;
                bool shouldReport = shouldReportNewError || shouldReportCriticalError || shouldReportRegression || shouldReportNewEvent || shouldReportCriticalEvent;

                Log.Trace().Message("Settings: newerror={0} criticalerror={1} regression={2} new={3} critical={4}",
                                    settings.ReportNewErrors, settings.ReportCriticalErrors,
                                    settings.ReportEventRegressions, settings.ReportNewEvents, settings.ReportCriticalEvents).WriteIf(shouldLog);
                Log.Trace().Message("Should process: newerror={0} criticalerror={1} regression={2} new={3} critical={4}",
                                    shouldReportNewError, shouldReportCriticalError,
                                    shouldReportRegression, shouldReportNewEvent, shouldReportCriticalEvent).WriteIf(shouldLog);

                var requestInfo = eventNotification.Event.GetRequestInfo();
                // check for known bots if the user has elected to not report them
                if (shouldReport && requestInfo != null && !String.IsNullOrEmpty(requestInfo.UserAgent))
                {
                    ClientInfo info = null;
                    try {
                        info = Parser.GetDefault().Parse(requestInfo.UserAgent);
                    } catch (Exception ex) {
                        Log.Warn().Project(eventNotification.Event.ProjectId).Message("Unable to parse user agent {0}. Exception: {1}",
                                                                                      requestInfo.UserAgent, ex.Message).Write();
                    }

                    var botPatterns = project.Configuration.Settings.ContainsKey(SettingsDictionary.KnownKeys.UserAgentBotPatterns)
                        ? project.Configuration.Settings.GetStringCollection(SettingsDictionary.KnownKeys.UserAgentBotPatterns).ToList()
                        : new List <string>();

                    if (info != null && info.Device.IsSpider || requestInfo.UserAgent.AnyWildcardMatches(botPatterns))
                    {
                        shouldReport = false;
                        Log.Info().Message("Skipping because event is from a bot \"{0}\".", requestInfo.UserAgent).WriteIf(shouldLog);
                    }
                }

                if (!shouldReport)
                {
                    continue;
                }

                var model = new EventNotificationModel(eventNotification)
                {
                    ProjectName      = project.Name,
                    TotalOccurrences = totalOccurrences
                };

                // don't send notifications in non-production mode to email addresses that are not on the outbound email list.
                if (Settings.Current.WebsiteMode != WebsiteMode.Production &&
                    !Settings.Current.AllowedOutboundAddresses.Contains(v => user.EmailAddress.ToLowerInvariant().Contains(v)))
                {
                    Log.Info().Message("Skipping because email is not on the outbound list and not in production mode.").WriteIf(shouldLog);
                    continue;
                }

                Log.Trace().Message("Sending email to {0}...", user.EmailAddress).Write();
                _mailer.SendNotice(user.EmailAddress, model);
                emailsSent++;
                Log.Trace().Message("Done sending email.").WriteIf(shouldLog);
            }

            // if we sent any emails, mark the last time a notification for this stack was sent.
            if (emailsSent > 0)
            {
                _cacheClient.Set(String.Concat("notify:stack-throttle:", eventNotification.Event.StackId), DateTime.Now, DateTime.Now.AddMinutes(15));
                Log.Info().Message("Notifications sent: event={0} stack={1} count={2}", eventNotification.Event.Id, eventNotification.Event.StackId, emailsSent).WriteIf(shouldLog);
            }

            queueEntry.Complete();

            return(JobResult.Success);
        }
Esempio n. 5
0
        public override void ProcessBatch(ICollection <EventContext> contexts)
        {
            var stacks = new Dictionary <string, Tuple <bool, Stack> >();

            foreach (var ctx in contexts)
            {
                if (String.IsNullOrEmpty(ctx.Event.StackId))
                {
                    // only add default signature info if no other signature info has been added
                    if (ctx.StackSignatureData.Count == 0)
                    {
                        ctx.StackSignatureData.Add("Type", ctx.Event.Type);
                        if (!String.IsNullOrEmpty(ctx.Event.Source))
                        {
                            ctx.StackSignatureData.Add("Source", ctx.Event.Source);
                        }
                    }

                    string signatureHash = ctx.StackSignatureData.Values.ToSHA1();
                    ctx.SignatureHash = signatureHash;

                    Tuple <bool, Stack> value;
                    if (stacks.TryGetValue(signatureHash, out value))
                    {
                        ctx.Stack = value.Item2;
                    }
                    else
                    {
                        ctx.Stack = _stackRepository.GetStackBySignatureHash(ctx.Event.ProjectId, signatureHash);
                        if (ctx.Stack != null)
                        {
                            stacks.Add(signatureHash, Tuple.Create(false, ctx.Stack));
                        }
                    }

                    if (ctx.Stack == null)
                    {
                        Log.Trace().Message("Creating new event stack.").Write();
                        ctx.IsNew = true;

                        string title = _formattingPluginManager.GetStackTitle(ctx.Event);
                        var    stack = new Stack {
                            OrganizationId                                      = ctx.Event.OrganizationId,
                            ProjectId                                           = ctx.Event.ProjectId,
                            SignatureInfo                                       = new SettingsDictionary(ctx.StackSignatureData),
                            SignatureHash                                       = signatureHash,
                            Title                                               = title != null?title.Truncate(1000) : null,
                                                               Tags             = ctx.Event.Tags ?? new TagSet(),
                                                               Type             = ctx.Event.Type,
                                                               TotalOccurrences = 1,
                                                               FirstOccurrence  = ctx.Event.Date.UtcDateTime,
                                                               LastOccurrence   = ctx.Event.Date.UtcDateTime
                        };

                        ctx.Stack = stack;
                        stacks.Add(signatureHash, Tuple.Create(true, ctx.Stack));
                    }
                }
                else
                {
                    ctx.Stack = _stackRepository.GetById(ctx.Event.StackId, true);
                    if (ctx.Stack == null || ctx.Stack.ProjectId != ctx.Event.ProjectId)
                    {
                        ctx.SetError("Invalid StackId.");
                        continue;
                    }

                    ctx.SignatureHash = ctx.Stack.SignatureHash;

                    if (!stacks.ContainsKey(ctx.Stack.SignatureHash))
                    {
                        stacks.Add(ctx.Stack.SignatureHash, Tuple.Create(false, ctx.Stack));
                    }
                    else
                    {
                        stacks[ctx.Stack.SignatureHash] = Tuple.Create(false, ctx.Stack);
                    }
                }

                if (!ctx.IsNew && ctx.Event.Tags != null && ctx.Event.Tags.Count > 0)
                {
                    if (ctx.Stack.Tags == null)
                    {
                        ctx.Stack.Tags = new TagSet();
                    }

                    List <string> newTags = ctx.Event.Tags.Where(t => !ctx.Stack.Tags.Contains(t)).ToList();
                    if (newTags.Count > 0)
                    {
                        ctx.Stack.Tags.AddRange(newTags);
                        // make sure the stack gets saved
                        if (!stacks.ContainsKey(ctx.Stack.SignatureHash))
                        {
                            stacks.Add(ctx.Stack.SignatureHash, Tuple.Create(true, ctx.Stack));
                        }
                        else
                        {
                            stacks[ctx.Stack.SignatureHash] = Tuple.Create(true, stacks[ctx.Stack.SignatureHash].Item2);
                        }
                    }
                }

                ctx.Event.IsFirstOccurrence = ctx.IsNew;

                // sync the fixed and hidden flags to the error occurrence
                ctx.Event.IsFixed  = ctx.Stack.DateFixed.HasValue;
                ctx.Event.IsHidden = ctx.Stack.IsHidden;
            }

            var stacksToAdd = stacks.Where(kvp => kvp.Value.Item1 && String.IsNullOrEmpty(kvp.Value.Item2.Id)).Select(kvp => kvp.Value.Item2).ToList();

            if (stacksToAdd.Count > 0)
            {
                _stackRepository.Add(stacksToAdd, true, sendNotification: stacksToAdd.Count == 1);
                if (stacksToAdd.Count > 1)
                {
                    _publisher.Publish(new EntityChanged {
                        ChangeType = ChangeType.Added, Type = typeof(Stack).Name, OrganizationId = contexts.First().Organization.Id, ProjectId = contexts.First().Project.Id
                    });
                }
            }

            var stacksToSave = stacks.Where(kvp => kvp.Value.Item1 && !String.IsNullOrEmpty(kvp.Value.Item2.Id)).Select(kvp => kvp.Value.Item2).ToList();

            if (stacksToSave.Count > 0)
            {
                _stackRepository.Save(stacksToSave, true, sendNotification: false); // notification will get sent later in the update stats step
            }
            // Set stack ids after they have been saved and created
            contexts.ForEach(ctx => {
                ctx.Event.StackId = ctx.Stack != null ? ctx.Stack.Id : null;
            });
        }
        public override void Process(EventContext ctx)
        {
            if (String.IsNullOrEmpty(ctx.Event.StackId))
            {
                if (_stackRepository == null)
                {
                    throw new InvalidOperationException("You must pass a non-null stackRepository parameter to the constructor.");
                }

                // only add default signature info if no other signature info has been added
                if (ctx.StackSignatureData.Count == 0)
                {
                    ctx.StackSignatureData.Add("Type", ctx.Event.Type);
                    if (!String.IsNullOrEmpty(ctx.Event.Source))
                    {
                        ctx.StackSignatureData.Add("Source", ctx.Event.Source);
                    }
                }

                string signatureHash = ctx.StackSignatureData.Values.Any(v => v != null) ? ctx.StackSignatureData.Values.ToSHA1() : null;
                ctx.SetProperty("__SignatureHash", signatureHash);
                ctx.Event.SummaryHtml = _pluginManager.GetEventSummaryHtml(ctx.Event);

                ctx.StackInfo = _stackRepository.GetStackInfoBySignatureHash(ctx.Event.ProjectId, signatureHash);
                if (ctx.StackInfo == null)
                {
                    Log.Trace().Message("Creating new error stack.").Write();
                    ctx.IsNew = true;
                    var stack = new Stack {
                        OrganizationId   = ctx.Event.OrganizationId,
                        ProjectId        = ctx.Event.ProjectId,
                        SignatureInfo    = new SettingsDictionary(ctx.StackSignatureData),
                        SignatureHash    = signatureHash,
                        Title            = _pluginManager.GetStackTitle(ctx.Event),
                        SummaryHtml      = _pluginManager.GetStackSummaryHtml(ctx.Event),
                        Tags             = ctx.Event.Tags ?? new TagSet(),
                        TotalOccurrences = 1,
                        FirstOccurrence  = ctx.Event.Date.UtcDateTime,
                        LastOccurrence   = ctx.Event.Date.UtcDateTime
                    };

                    ctx.Stack     = _stackRepository.Add(stack, true);
                    ctx.StackInfo = new StackInfo {
                        Id        = stack.Id,
                        DateFixed = stack.DateFixed,
                        OccurrencesAreCritical = stack.OccurrencesAreCritical
                    };

                    // new 404 stack id added, invalidate 404 id cache
                    if (ctx.Event.IsNotFound)
                    {
                        _stackRepository.InvalidateNotFoundIdsCache(ctx.Event.ProjectId);
                    }
                }

                Log.Trace().Message("Updating error's ErrorStackId to: {0}", ctx.StackInfo.Id).Write();
                ctx.Event.StackId = ctx.StackInfo.Id;
            }
            else
            {
                ctx.Stack = _stackRepository.GetById(ctx.Event.StackId);

                // TODO: Update unit tests to work with this check.
                //if (stack == null || stack.ProjectId != error.ProjectId)
                //    throw new InvalidOperationException("Invalid ErrorStackId.");
                if (ctx.Stack == null)
                {
                    return;
                }

                if (ctx.Event.Tags != null && ctx.Event.Tags.Count > 0)
                {
                    if (ctx.Stack.Tags == null)
                    {
                        ctx.Stack.Tags = new TagSet();
                    }

                    List <string> newTags = ctx.Event.Tags.Where(t => !ctx.Stack.Tags.Contains(t)).ToList();
                    if (newTags.Count > 0)
                    {
                        ctx.Stack.Tags.AddRange(newTags);
                        _stackRepository.Save(ctx.Stack);
                    }
                }

                ctx.StackInfo = new StackInfo {
                    Id        = ctx.Stack.Id,
                    DateFixed = ctx.Stack.DateFixed,
                    OccurrencesAreCritical = ctx.Stack.OccurrencesAreCritical,
                    IsHidden = ctx.Stack.IsHidden
                };
            }

            // sync the fixed and hidden flags to the error occurrence
            ctx.Event.IsFixed  = ctx.StackInfo.DateFixed.HasValue;
            ctx.Event.IsHidden = ctx.StackInfo.IsHidden;
        }
Esempio n. 7
0
        public static WebHookEvent FromEvent(EventContext ctx, IProjectRepository projectRepository, IStackRepository stackRepository, IOrganizationRepository organizationRepository)
        {
            if (ctx == null || ctx.Event == null)
            {
                throw new ArgumentNullException("ctx");
            }

            if (projectRepository == null)
            {
                throw new ArgumentNullException("projectRepository");
            }

            if (stackRepository == null)
            {
                throw new ArgumentNullException("stackRepository");
            }

            if (organizationRepository == null)
            {
                throw new ArgumentNullException("organizationRepository");
            }

            var project = projectRepository.GetById(ctx.Event.ProjectId);

            if (project == null)
            {
                throw new ArgumentException("ProjectId not found.");
            }

            var organization = organizationRepository.GetById(ctx.Event.OrganizationId);

            if (organization == null)
            {
                throw new ArgumentException("OrganizationId not found.");
            }

            var errorStack = stackRepository.GetById(ctx.Event.StackId);

            if (errorStack == null)
            {
                throw new ArgumentException("ErrorStackId not found.");
            }

            return(new WebHookEvent {
                Id = ctx.Event.Id,
                OccurrenceDate = ctx.Event.Date,
                Tags = ctx.Event.Tags,
                Message = ctx.Event.Message,
                Type = ctx.Event.Type,
                Source = ctx.Event.Source,
                ProjectId = ctx.Event.ProjectId,
                ProjectName = project.Name,
                OrganizationId = ctx.Event.OrganizationId,
                OrganizationName = organization.Name,
                StackId = ctx.Event.StackId,
                StackTitle = errorStack.Title,
                StackDescription = errorStack.Description,
                StackTags = errorStack.Tags,
                TotalOccurrences = errorStack.TotalOccurrences,
                FirstOccurrence = errorStack.FirstOccurrence,
                LastOccurrence = errorStack.LastOccurrence,
                DateFixed = errorStack.DateFixed,
                IsRegression = ctx.IsRegression,
                IsNew = ctx.IsNew
            });
        }