public async Task should_be_able_to_identity_tags_when_only_multiple_tags_are_specified_with_spaces()
        {
            var repos    = Substitute.For <ITagsRepository>();
            var provider = Substitute.For <ITagIdentifierProvider>();

            provider.GetIdentifiers(Arg.Any <TagIdentifierContext>()).Returns(new ITagIdentifier[0]);
            var ctx      = Substitute.For <IMessageContext>();
            var incident = new IncidentSummaryDTO(1, "Ada");
            var report   = new ReportDTO
            {
                ContextCollections = new[]
                { new ContextCollectionDTO("Data", new Dictionary <string, string> {
                        { "ErrTags[]", "MyTag" }, { "ErrTags[]", "YourTag" }
                    }) }
            };
            var e = new ReportAddedToIncident(incident, report, false);

            var sut = new IdentifyTagsFromIncident(repos, provider);
            await sut.HandleAsync(ctx, e);

            var arguments = repos.ReceivedCalls().First(x => x.GetMethodInfo().Name == "AddAsync")
                            .GetArguments();
            var tags = (Tag[])arguments[1];

            tags[0].Name.Should().Be("MyTag");
            tags[1].Name.Should().Be("YourTag");
        }
        /// <summary>
        ///     Creates a new instance of <see cref="ReportAddedToIncident" />.
        /// </summary>
        /// <param name="incident">incident that the report was added to</param>
        /// <param name="report">received report</param>
        /// <param name="isReOpened">Incident was marked as closed, so the received report opened it again.</param>
        /// <exception cref="ArgumentNullException">incident;report</exception>
        public ReportAddedToIncident(IncidentSummaryDTO incident, ReportDTO report, bool isReOpened)
        {
            if (incident == null)
            {
                throw new ArgumentNullException("incident");
            }
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }

            Incident   = incident;
            Report     = report;
            IsReOpened = isReOpened;
        }
        public async Task Should_be_able_to_use_position_in_regular_collection()
        {
            var repos    = Substitute.For <IErrorOriginRepository>();
            var incident = new IncidentSummaryDTO(1, "Hello");
            var data     = new Dictionary <string, string> {
                { "ReportLongitude", "60.6065" }, { "ReportLatitude", "15.6355" }
            };
            var report = new ReportDTO {
                ContextCollections = new[] { new ContextCollectionDTO("SomeCollection", data) }
            };
            var e             = new ReportAddedToIncident(incident, report, false);
            var context       = Substitute.For <IMessageContext>();
            var configWrapper = Substitute.For <IConfiguration <OriginsConfiguration> >();

            var sut = new StorePositionFromNewReport(repos, configWrapper);
            await sut.HandleAsync(context, e);

            var entity = (ErrorOrigin)repos.ReceivedCalls().First(x => x.GetMethodInfo().Name == "CreateAsync")
                         .GetArguments().First();

            entity.Latitude.Should().Be(15.6355);
            entity.Longitude.Should().Be(60.6065);
        }
示例#4
0
        /// <summary>
        ///     Send
        /// </summary>
        /// <param name="idOrEmailAddress">Account id or email address</param>
        /// <param name="incident">Incident that the report belongs to</param>
        /// <param name="report">Report being processed.</param>
        /// <returns>task</returns>
        /// <exception cref="ArgumentNullException">idOrEmailAddress; incident; report</exception>
        public async Task SendAsync(string idOrEmailAddress, IncidentSummaryDTO incident, ReportDTO report)
        {
            if (idOrEmailAddress == null)
            {
                throw new ArgumentNullException("idOrEmailAddress");
            }
            if (incident == null)
            {
                throw new ArgumentNullException("incident");
            }
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }

            var config = ConfigurationStore.Instance.Load <BaseConfiguration>();

            var shortName = incident.Name.Length > 40
                ? incident.Name.Substring(0, 40) + "..."
                : incident.Name;

            // need to be safe for subjects
            shortName = shortName.Replace("\n", ";");

            var baseUrl = string.Format("{0}/#/application/{1}/incident/{2}",
                                        config.BaseUrl.ToString().TrimEnd('/'),
                                        report.ApplicationId,
                                        report.IncidentId);

            //TODO: Add more information
            var msg = new EmailMessage(idOrEmailAddress);

            if (incident.IsReOpened)
            {
                msg.Subject  = "ReOpened: " + shortName;
                msg.TextBody = string.Format(@"Incident: {0}
Report url: {0}/report/{1}
Description: {2}
Exception: {3}

{4}
", baseUrl, report.Id, incident.Name, report.Exception.FullName, report.Exception.StackTrace);
            }
            else if (incident.ReportCount == 1)
            {
                msg.Subject  = "New: " + shortName;
                msg.TextBody = string.Format(@"Incident: {0}
Description: {1}
Exception: {2}

{3}", baseUrl, incident.Name, report.Exception.FullName, report.Exception.StackTrace);
            }
            else
            {
                msg.Subject  = "Updated: " + shortName;
                msg.TextBody = string.Format(@"Incident: {0}
Report url: {0}/report/{1}
Description: {2}
Exception: {3}

{4}
", baseUrl, report.Id, incident.Name, report.Exception.FullName, report.Exception.StackTrace);
            }

            var emailCmd = new SendEmail(msg);
            await _commandBus.ExecuteAsync(emailCmd);
        }
        /// <summary>
        ///     Analyze report
        /// </summary>
        /// <param name="report">report</param>
        /// <exception cref="ArgumentNullException">report</exception>
        public async Task Analyze(IMessageContext context, ErrorReportEntity report)
        {
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }

            var countThisMonth = _repository.GetMonthReportCount();

            if (countThisMonth >= 500)
            {
                _repository.AddMissedReport(DateTime.Today);
                return;
            }

            var exists = _repository.ExistsByClientId(report.ClientReportId);

            if (exists)
            {
                _logger.Warn("Report have already been uploaded: " + report.ClientReportId);
                return;
            }

            try
            {
                var hashCode = _hashCodeGenerator.GenerateHashCode(report);
                report.Init(hashCode);
            }
            catch (Exception ex)
            {
                var reportJson = JsonConvert.SerializeObject(report);
                if (reportJson.Length > 1000000)
                {
                    reportJson = reportJson.Substring(0, 100000) + "[....]";
                }
                _logger.Fatal("Failed to init report " + reportJson, ex);
                return;
            }

            var applicationVersion = GetVersionFromReport(report);
            var isReOpened         = false;
            var firstLine          = report.GenerateHashCodeIdentifier();
            var incident           = _repository.FindIncidentForReport(report.ApplicationId, report.ReportHashCode, firstLine);

            if (incident == null)
            {
                incident = BuildIncident(report);
                _repository.CreateIncident(incident);

                var evt = new IncidentCreated(incident.ApplicationId,
                                              incident.Id, incident.Description, incident.FullName)
                {
                    CreatedAtUtc       = incident.CreatedAtUtc,
                    ApplicationVersion = applicationVersion,
                };

                await _domainQueue.PublishAsync(context.Principal, evt);

                await context.SendAsync(evt);
            }
            else
            {
                await _repository.StoreReportStats(new ReportMapping()
                {
                    IncidentId    = incident.Id,
                    ErrorId       = report.ClientReportId,
                    ReceivedAtUtc = report.CreatedAtUtc
                });

                if (incident.IsIgnored)
                {
                    _logger.Info("Incident is ignored: " + JsonConvert.SerializeObject(report));
                    incident.WasJustIgnored();
                    _repository.UpdateIncident(incident);
                    return;
                }

                if (incident.IsClosed)
                {
                    if (applicationVersion != null && incident.IsReportIgnored(applicationVersion))
                    {
                        _logger.Info("Ignored report since it's for a version less that the solution version: " + JsonConvert.SerializeObject(report));
                        incident.WasJustIgnored();
                        _repository.UpdateIncident(incident);
                        return;
                    }

                    isReOpened = true;
                    incident.ReOpen();
                    var evt = new IncidentReOpened(incident.ApplicationId, incident.Id,
                                                   incident.CreatedAtUtc)
                    {
                        ApplicationVersion = applicationVersion
                    };
                    await context.SendAsync(evt);

                    await _domainQueue.PublishAsync(context.Principal, evt);
                }

                incident.AddReport(report);
                _repository.UpdateIncident(incident);
                if (incident.ReportCount > 25)
                {
                    _logger.Debug("Report count is more than 25. Storing only report stats for incident " + incident.Id);
                    return;
                }
            }

            if (!string.IsNullOrWhiteSpace(report.EnvironmentName))
            {
                _repository.SaveEnvironmentName(incident.Id, report.EnvironmentName);
            }

            report.IncidentId = incident.Id;
            _repository.CreateReport(report);
            _logger.Debug("saving report " + report.Id + " for incident " + incident.Id);
            var appName = _repository.GetAppName(incident.ApplicationId);

            var summary = new IncidentSummaryDTO(incident.Id, incident.Description)
            {
                ApplicationId   = incident.ApplicationId,
                ApplicationName = appName,
                CreatedAtUtc    = incident.CreatedAtUtc,
                LastUpdateAtUtc = incident.UpdatedAtUtc,
                IsReOpened      = incident.IsReOpened,
                Name            = incident.Description,
                ReportCount     = incident.ReportCount
            };
            var sw = new Stopwatch();

            sw.Start();
            _logger.Debug("Publishing now: " + report.ClientReportId);
            var e = new ReportAddedToIncident(summary, ConvertToCoreReport(report, applicationVersion), isReOpened);
            await context.SendAsync(e);

            await context.SendAsync(new ProcessInboundContextCollections());

            if (sw.ElapsedMilliseconds > 200)
            {
                _logger.Debug("PublishAsync took " + sw.ElapsedMilliseconds);
            }
            sw.Stop();
        }
示例#6
0
        /// <summary>
        ///     Send
        /// </summary>
        /// <param name="accountId">Account to send to</param>
        /// <param name="incident">Incident that the report belongs to</param>
        /// <param name="report">report being processed</param>
        /// <returns>task</returns>
        /// <exception cref="ArgumentNullException">incident;report</exception>
        /// <exception cref="ArgumentOutOfRangeException">accountId</exception>
        public async Task SendAsync(int accountId, IncidentSummaryDTO incident, ReportDTO report)
        {
            if (incident == null)
            {
                throw new ArgumentNullException("incident");
            }
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }
            if (accountId <= 0)
            {
                throw new ArgumentOutOfRangeException("accountId");
            }

            var settings = await _userRepository.GetUserAsync(accountId);

            if (string.IsNullOrEmpty(settings.MobileNumber))
            {
                return; //TODO: LOG
            }
            var config    = ConfigurationStore.Instance.Load <BaseConfiguration>();
            var url       = config.BaseUrl;
            var shortName = incident.Name.Length > 20
                ? incident.Name.Substring(0, 20) + "..."
                : incident.Name;

            var exMsg = report.Exception.Message.Length > 100
                ? report.Exception.Message.Substring(0, 100)
                : report.Exception.Message;

            string msg;

            if (incident.IsReOpened)
            {
                msg = string.Format(@"ReOpened: {0}
{1}/#/incident/{2}

{3}", shortName, url, incident.Id, exMsg);
            }
            else if (incident.ReportCount == 1)
            {
                msg = string.Format(@"New: {0}
{1}/#/incident/{2}

Exception: {3}", shortName, url, incident.Id, exMsg);
            }
            else
            {
                msg = string.Format(@"Updated: {0}
ReportCount: {4}
{1}/#/incident/{2}

{3}", shortName, url, incident.Id, exMsg, incident.ReportCount);
            }

            var iso      = Encoding.GetEncoding("ISO-8859-1");
            var utfBytes = Encoding.UTF8.GetBytes(msg);
            var isoBytes = Encoding.Convert(Encoding.UTF8, iso, utfBytes);

            msg = iso.GetString(isoBytes);

            var request =
                WebRequest.CreateHttp("https://web.smscom.se/sendsms.aspx?acc=ip1-755&pass=z35llww4&msg=" +
                                      Uri.EscapeDataString(msg) + "&to=" + settings.MobileNumber +
                                      "&from=OneTrueError&prio=2");

            request.ContentType = "application/json";
            request.Method      = "GET";
            await request.GetResponseAsync();
        }
        /// <summary>
        ///     Send
        /// </summary>
        /// <param name="accountId">Account to send to</param>
        /// <param name="incident">Incident that the report belongs to</param>
        /// <param name="report">report being processed</param>
        /// <returns>task</returns>
        /// <exception cref="ArgumentNullException">incident;report</exception>
        /// <exception cref="ArgumentOutOfRangeException">accountId</exception>
        public async Task SendAsync(int accountId, IncidentSummaryDTO incident, ReportDTO report)
        {
            if (incident == null)
            {
                throw new ArgumentNullException("incident");
            }
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }
            if (accountId <= 0)
            {
                throw new ArgumentOutOfRangeException("accountId");
            }

            var settings = await _userRepository.GetUserAsync(accountId);

            if (string.IsNullOrEmpty(settings.MobileNumber))
            {
                return; //TODO: LOG
            }
            var url       = _baseConfiguration.BaseUrl;
            var shortName = incident.Name.Length > 20
                ? incident.Name.Substring(0, 20) + "..."
                : incident.Name;

            var exMsg = report.Exception.Message.Length > 100
                ? report.Exception.Message.Substring(0, 100)
                : report.Exception.Message;


            var baseUrl     = _baseConfiguration.BaseUrl.ToString().TrimEnd('/');
            var incidentUrl =
                $"{baseUrl}/discover/incidents/{report.ApplicationId}/incident/{report.IncidentId}/";

            string msg;

            if (incident.IsReOpened)
            {
                msg = $@"ReOpened: {shortName}
{incidentUrl}

{exMsg}";
            }
            else if (incident.ReportCount == 1)
            {
                msg = $@"New: {shortName}
{incidentUrl}

{exMsg}";
            }
            else
            {
                msg = $@"Updated: {shortName}
ReportCount: {incident.ReportCount}
{incidentUrl}

{exMsg}";
            }

            var iso      = Encoding.GetEncoding("ISO-8859-1");
            var utfBytes = Encoding.UTF8.GetBytes(msg);
            var isoBytes = Encoding.Convert(Encoding.UTF8, iso, utfBytes);

            msg = iso.GetString(isoBytes);

            var request =
                WebRequest.CreateHttp("https://web.smscom.se/sendsms.aspx?acc=ip1-755&pass=z35llww4&msg=" +
                                      Uri.EscapeDataString(msg) + "&to=" + settings.MobileNumber +
                                      "&from=Coderr&prio=2");

            request.ContentType = "application/json";
            request.Method      = "GET";
            await request.GetResponseAsync();
        }
示例#8
0
        /// <summary>
        ///     Analyze report
        /// </summary>
        /// <param name="report">report</param>
        /// <exception cref="ArgumentNullException">report</exception>
        public void Analyze(ErrorReportEntity report)
        {
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }

            var exists = _repository.ExistsByClientId(report.ClientReportId);

            if (exists)
            {
                _logger.Warn("Report have already been uploaded: " + report.ClientReportId);
                return;
            }

            try
            {
                var hashCode = _hashCodeGenerator.GenerateHashCode(report);
                report.Init(hashCode);
            }
            catch (Exception ex)
            {
                _logger.Fatal("Failed to store report " + JsonConvert.SerializeObject(report), ex);
                return;
            }

            var isReOpened = false;
            var firstLine  = report.GenerateHashCodeIdentifier();
            var incident   = _repository.FindIncidentForReport(report.ApplicationId, report.ReportHashCode, firstLine);

            if (incident == null)
            {
                incident = BuildIncident(report);
                _repository.CreateIncident(incident);
            }
            else
            {
                if (incident.ReportCount > 10000)
                {
                    _logger.Debug("Reportcount is more than 10000. Ignoring report for incident " + incident.Id);
                    return;
                }

                if (incident.IsIgnored)
                {
                    _logger.Info("Incident is ignored: " + JsonConvert.SerializeObject(report));
                    incident.WasJustIgnored();
                    _repository.UpdateIncident(incident);
                    return;
                }
                if (incident.IsSolved)
                {
                    isReOpened = true;
                    incident.ReOpen();
                    _eventBus.PublishAsync(new IncidentReOpened(incident.ApplicationId, incident.Id,
                                                                incident.CreatedAtUtc));
                }

                incident.AddReport(report);
                _repository.UpdateIncident(incident);
            }

            report.IncidentId = incident.Id;
            _repository.CreateReport(report);
            _logger.Debug("saving report " + report.Id + " for incident " + incident.Id);
            var appName = _repository.GetAppName(incident.ApplicationId);

            var summary = new IncidentSummaryDTO(incident.Id, incident.Description)
            {
                ApplicationId   = incident.ApplicationId,
                ApplicationName = appName,
                CreatedAtUtc    = incident.CreatedAtUtc,
                LastUpdateAtUtc = incident.UpdatedAtUtc,
                IsReOpened      = incident.IsReOpened,
                Name            = incident.Description,
                ReportCount     = incident.ReportCount
            };
            var sw = new Stopwatch();

            sw.Start();
            _logger.Debug("Publishing now: " + report.ClientReportId);
            var e = new ReportAddedToIncident(summary, ConvertToCoreReport(report), isReOpened);

            _eventBus.PublishAsync(e);
            if (sw.ElapsedMilliseconds > 200)
            {
                _logger.Debug("Publish took " + sw.ElapsedMilliseconds);
            }
            sw.Stop();
        }
示例#9
0
        /// <summary>
        ///     Analyze report
        /// </summary>
        /// <param name="report">report</param>
        /// <exception cref="ArgumentNullException">report</exception>
        public async Task Analyze(IMessageContext context, ErrorReportEntity report)
        {
            if (report == null)
            {
                throw new ArgumentNullException(nameof(report));
            }

            var countThisMonth = _repository.GetMonthReportCount();

            if (countThisMonth >= 500)
            {
                _repository.AddMissedReport(DateTime.Today);
                return;
            }

            _logger.Debug("Running as " + context.Principal.ToFriendlyString());
            var exists = _repository.ExistsByClientId(report.ClientReportId);

            if (exists)
            {
                _logger.Warn($"Report have already been uploaded: {report.ClientReportId} for {report.RemoteAddress}.");
                return;
            }

            ErrorHashCode hashcodeResult;

            try
            {
                hashcodeResult = _hashCodeGenerator.GenerateHashCode(report);
                report.Init(hashcodeResult.HashCode);
            }
            catch (Exception ex)
            {
                var reportJson = JsonConvert.SerializeObject(report);
                if (reportJson.Length > 1000000)
                {
                    reportJson = reportJson.Substring(0, 100000) + "[....]";
                }
                _logger.Fatal($"Failed to init report {reportJson}", ex);
                return;
            }

            var storeReport        = true;
            var applicationVersion = GetVersionFromReport(report);
            var isReOpened         = false;

            IncidentBeingAnalyzed incident = null;

            if (hashcodeResult.CompabilityHashSource != null)
            {
                incident = _repository.FindIncidentForReport(report.ApplicationId, hashcodeResult.CompabilityHashSource, hashcodeResult.CollisionIdentifier);
                if (incident != null)
                {
                    report.Init(hashcodeResult.CompabilityHashSource);
                }
            }
            if (incident == null)
            {
                incident = _repository.FindIncidentForReport(report.ApplicationId, report.ReportHashCode, hashcodeResult.CollisionIdentifier);
            }

            var isNewIncident = false;

            if (incident == null)
            {
                if (report.Exception == null)
                {
                    _logger.Debug("Got no exception");
                }

                isNewIncident = true;
                incident      = BuildIncident(report);
                _repository.CreateIncident(incident);
                await _repository.StoreReportStats(new ReportMapping()
                {
                    IncidentId    = incident.Id,
                    ErrorId       = report.ClientReportId,
                    ReceivedAtUtc = report.CreatedAtUtc
                });

                var evt = new IncidentCreated(incident.ApplicationId,
                                              incident.Id, incident.Description, incident.FullName)
                {
                    CreatedAtUtc       = incident.CreatedAtUtc,
                    ApplicationVersion = applicationVersion,
                };
                _logger.Info($"Storing IncidentCreated with {context.Principal.ToFriendlyString()}: {JsonConvert.SerializeObject(evt)}");
                await _domainQueue.PublishAsync(context.Principal, evt);

                await context.SendAsync(evt);
            }
            else
            {
                if (incident.IsIgnored)
                {
                    _logger.Info("Incident is ignored: " + JsonConvert.SerializeObject(report));
                    incident.WasJustIgnored();
                    _repository.UpdateIncident(incident);
                    return;
                }

                // Do this before checking closed
                // as we want to see if it still gets reports.
                var stat = new ReportMapping()
                {
                    IncidentId    = incident.Id,
                    ErrorId       = report.ClientReportId,
                    ReceivedAtUtc = report.CreatedAtUtc
                };
                await _repository.StoreReportStats(stat);

                _logger.Debug("Storing stats " + JsonConvert.SerializeObject(stat));

                if (incident.IsClosed)
                {
                    if (applicationVersion != null && incident.IsReportIgnored(applicationVersion))
                    {
                        _logger.Info("Ignored report since it's for a version less that the solution version: " + JsonConvert.SerializeObject(report));
                        incident.WasJustIgnored();
                        _repository.UpdateIncident(incident);
                        return;
                    }

                    isReOpened = true;
                    incident.ReOpen();
                    var evt = new IncidentReOpened(incident.ApplicationId, incident.Id,
                                                   incident.CreatedAtUtc)
                    {
                        ApplicationVersion = applicationVersion
                    };
                    await context.SendAsync(evt);

                    await _domainQueue.PublishAsync(context.Principal, evt);
                }

                incident.AddReport(report);

                // Let's continue to receive reports once a day when
                // limit is reached (to get more fresh data, and still not load the system unnecessary).
                var timesSinceLastReport = DateTime.UtcNow.Subtract(incident.LastStoredReportUtc);
                if (incident.ReportCount > _reportConfig.Value.MaxReportsPerIncident &&
                    timesSinceLastReport < TimeSpan.FromMinutes(10))
                {
                    _repository.UpdateIncident(incident);
                    _logger.Debug($"Report count is more than {_reportConfig.Value.MaxReportsPerIncident}. Ignoring reports for incident {incident.Id}. Minutes since last report: " + timesSinceLastReport.TotalMinutes);
                    storeReport = false;
                    //don't exit here, since we want to be able to process reports
                }
            }

            if (!string.IsNullOrWhiteSpace(report.EnvironmentName))
            {
                _repository.SaveEnvironmentName(incident.Id, report.EnvironmentName);
            }

            report.IncidentId = incident.Id;

            if (storeReport)
            {
                incident.LastStoredReportUtc = DateTime.UtcNow;
                _repository.UpdateIncident(incident);
                _repository.CreateReport(report);
                _logger.Debug($"saving report {report.Id} for incident {incident.Id}");
            }

            var appName = _repository.GetAppName(incident.ApplicationId);
            var summary = new IncidentSummaryDTO(incident.Id, incident.Description)
            {
                ApplicationId   = incident.ApplicationId,
                ApplicationName = appName,
                CreatedAtUtc    = incident.CreatedAtUtc,
                LastUpdateAtUtc = incident.UpdatedAtUtc,
                IsReOpened      = incident.IsReOpened,
                Name            = incident.Description,
                ReportCount     = incident.ReportCount
            };
            var sw = new Stopwatch();

            sw.Start();
            var e = new ReportAddedToIncident(summary, ConvertToCoreReport(report, applicationVersion), isReOpened)
            {
                IsNewIncident   = isNewIncident,
                IsStored        = storeReport,
                EnvironmentName = report.EnvironmentName
            };
            await context.SendAsync(e);

            if (storeReport)
            {
                await context.SendAsync(new ProcessInboundContextCollections());
            }

            if (sw.ElapsedMilliseconds > 200)
            {
                _logger.Debug($"PublishAsync took {sw.ElapsedMilliseconds}");
            }
            sw.Stop();
        }
        /// <summary>
        ///     Send
        /// </summary>
        /// <param name="idOrEmailAddress">Account id or email address</param>
        /// <param name="incident">Incident that the report belongs to</param>
        /// <param name="report">Report being processed.</param>
        /// <returns>task</returns>
        /// <exception cref="ArgumentNullException">idOrEmailAddress; incident; report</exception>
        public async Task SendAsync(IMessageContext context, string idOrEmailAddress, IncidentSummaryDTO incident,
                                    ReportDTO report)
        {
            if (idOrEmailAddress == null)
            {
                throw new ArgumentNullException("idOrEmailAddress");
            }
            if (incident == null)
            {
                throw new ArgumentNullException("incident");
            }
            if (report == null)
            {
                throw new ArgumentNullException("report");
            }

            var shortName = incident.Name.Length > 40
                ? incident.Name.Substring(0, 40) + "..."
                : incident.Name;

            var pos = shortName.IndexOfAny(new[] { '\r', '\n' });

            if (pos != -1)
            {
                shortName = shortName.Substring(0, pos) + "[...]";
            }


            var baseUrl     = _baseConfiguration.BaseUrl.ToString().TrimEnd('/');
            var incidentUrl =
                $"{baseUrl}/discover/incidents/{report.ApplicationId}/incident/{report.IncidentId}/";

            //TODO: Add more information
            var msg = new EmailMessage(idOrEmailAddress);

            if (incident.IsReOpened)
            {
                msg.Subject  = "ReOpened: " + shortName;
                msg.TextBody = $@"{incident.Name}
{report.Exception.FullName}
{report.Exception.StackTrace}

{incidentUrl}";
            }
            else if (incident.ReportCount == 1)
            {
                msg.Subject  = "New: " + shortName;
                msg.TextBody = $@"{incident.Name}
{report.Exception.FullName}
{report.Exception.StackTrace}

{incidentUrl}";
            }
            else
            {
                msg.Subject  = "Updated: " + shortName;
                msg.TextBody = $@"{incident.Name}
{report.Exception.FullName}
{report.Exception.StackTrace}

{incidentUrl}";
            }

            var emailCmd = new SendEmail(msg);
            await context.SendAsync(emailCmd);
        }