/// <summary> /// Composes a component history array. /// </summary> /// <param name="cache">Extended cache object.</param> /// <param name="componentId">Component identifier.</param> /// <param name="componentName">Component name.</param> /// <returns>Complete component history object.</returns> public static async Task <InfrastructureComponentSummaryWithHistory[]> GetInfrastructureHistory(this IInfrastructureScoreCache cache, string componentId, string componentName) { var today = DateTimeOffset.UtcNow.Date; var infrastructureCategory = GetCategory(componentId); var components = new List <InfrastructureComponentSummaryWithHistory>(); foreach (var date in Enumerable.Range(-30, 31).Select(i => today.AddDays(i))) { var currentSummary = await cache.GetCountersSummary(componentId, date); var component = new InfrastructureComponentSummaryWithHistory { Date = date, Component = new InfrastructureComponent(componentId) { Category = infrastructureCategory, Name = componentName, }, Current = currentSummary, }; components.Add(component); } return(components.ToArray()); }
/// <summary> /// Returns infrastructure history for requested component. /// </summary> /// <param name="componentId">Component identifier.</param> /// <param name="date">The date to get details for.</param> /// <returns>Component details.</returns> public async Task <InfrastructureComponentSummaryWithHistory> GetDetails(string componentId, DateTime date) { // 1. get audit which represents the "date". var audit = await this.db.Set <AuditEntity>() .Include(i => i.InfrastructureComponent) .AsNoTracking() .Where(i => i.Date.Date == date.Date && i.ComponentId == componentId) .OrderByDescending(i => i.Date) .FirstOrDefaultAsync(); // 1.1. try to get InfrastructureComponent if no audit fo a requested date string componentName; if (audit == null) { var component = await this.db.Set <InfrastructureComponentEntity>() .AsNoTracking() .FirstOrDefaultAsync(i => i.ComponentId == componentId); if (component == null) { throw new JosekiException($"Unknown componentId: {componentId}"); } componentName = component.ComponentName; } else { componentName = audit.InfrastructureComponent.ComponentName; } // 2. prepare history for the last month var today = DateTime.UtcNow.Date; var componentHistory = new List <ScoreHistoryItem>(); foreach (var summaryDate in Enumerable.Range(-30, 31).Select(i => today.AddDays(i))) { var historyItem = await this.cache.GetCountersSummary(componentId, summaryDate); componentHistory.Add(new ScoreHistoryItem(summaryDate, historyItem.Score)); } // 3. when the history is ready, calculate summary for the requested date. var currentSummary = await this.cache.GetCountersSummary(componentId, date.Date); var componentDetails = new InfrastructureComponentSummaryWithHistory { Date = date, Component = new InfrastructureComponent(componentId) { Category = InfraScoreExtensions.GetCategory(componentId), Name = componentName, }, Current = currentSummary, ScoreHistory = componentHistory.OrderBy(i => i.RecordedAt).ToArray(), }; // if no audit for a given date - return result if (audit == null) { componentDetails.Checks = new Check[0]; componentDetails.CategorySummaries = new CheckCategorySummary[0]; return(componentDetails); } // 4. Get all the check details var checkResults = await this.db.Set <CheckResultEntity>() .AsNoTracking() .Include(i => i.Check) .Where(i => i.AuditId == audit.Id) .Select(i => new { i.ComponentId, i.Check.CheckId, i.Check.Severity, i.Check.Category, i.Check.Description, i.Value, i.Message, }) .ToArrayAsync(); var checks = new List <Check>(); foreach (var entity in checkResults) { CheckResult checkResult; switch (entity.Value) { case CheckValue.Failed: if (entity.Severity == CheckSeverity.Critical || entity.Severity == CheckSeverity.High) { checkResult = CheckResult.Failed; } else { checkResult = CheckResult.Warning; } break; case CheckValue.Succeeded: checkResult = CheckResult.Success; break; default: checkResult = CheckResult.NoData; break; } var message = entity.Message ?? entity.Description; var(collection, resource, tags) = ParseCollectionAndResource(entity.ComponentId); resource.Owner = await this.ownershipCache.GetOwner(entity.ComponentId); var check = new Check( date, collection, resource, entity.Category, new CheckControl(entity.CheckId, message), checkResult) { Tags = tags, }; checks.Add(check); } componentDetails.Checks = checks.ToArray(); // 5. enrich with category descriptions var scannerType = audit.InfrastructureComponent.ScannerId.Split('/').First(); var categories = checks .Select(i => i.Category) .Distinct() .Select(i => new { Id = $"{scannerType}.{i.Replace(" ", string.Empty)}".ToLowerInvariant(), Category = i }) .ToDictionary(i => i.Id, j => j.Category); var ids = categories.Keys.ToArray(); var docs = await this.docsHandler.GetItemsByIds(ids); componentDetails.CategorySummaries = docs .Select(i => new CheckCategorySummary { Description = i.Content, Category = categories[i.Id] }) .ToArray(); return(componentDetails); }
/// <summary> /// Composes an infrastructure overview object for a requested date and provided audits. /// </summary> /// <param name="cache">Extended cache object.</param> /// <param name="date">The date to calculate overview for.</param> /// <param name="audits">List of audits to include into overview.</param> /// <returns>Complete infrastructure-overview object.</returns> public static async Task <InfrastructureOverview> GetInfrastructureOverview(this IInfrastructureScoreCache cache, DateTime date, Audit[] audits) { // 0. if no audits - return stub if (audits.Length == 0) { return(new InfrastructureOverview { Overall = new InfrastructureComponentSummaryWithHistory { Component = new InfrastructureComponent(Audit.OverallId) { Category = InfrastructureCategory.Overall, Name = Audit.OverallName, }, Date = date, ScoreHistory = new ScoreHistoryItem[0], Current = new CountersSummary(), }, Components = new InfrastructureComponentSummaryWithHistory[0], }); } // 1. Calculate Overall data first, as it reloads caches for component audits automatically var overallHistory = new List <ScoreHistoryItem>(); foreach (var dateTime in audits.Select(i => i.Date.Date).Distinct()) { var historyItem = await cache.GetCountersSummary(Audit.OverallId, dateTime); overallHistory.Add(new ScoreHistoryItem(dateTime, historyItem.Score)); } var overall = new InfrastructureComponentSummaryWithHistory { Component = new InfrastructureComponent(Audit.OverallId) { Category = InfrastructureCategory.Overall, Name = Audit.OverallName, }, Date = date, ScoreHistory = overallHistory.OrderBy(i => i.RecordedAt).ToArray(), Current = await cache.GetCountersSummary(Audit.OverallId, date), }; // 2. Calculate each component data // The code expects that each date has only unique component-identifiers // Thus, iterating only over requested date - gives unique components var today = DateTime.UtcNow.Date; var month = Enumerable.Range(-30, 31).Select(i => today.AddDays(i)).ToArray(); var components = new List <InfrastructureComponentSummaryWithHistory>(); foreach (var audit in audits.Where(i => i.Date.Date == date.Date)) { // first, get counters for each date during last month var componentHistory = new List <ScoreHistoryItem>(); foreach (var summaryDate in month) { var historyItem = await cache.GetCountersSummary(audit.ComponentId, summaryDate); componentHistory.Add(new ScoreHistoryItem(summaryDate, historyItem.Score)); } // when the history is ready, calculate summary for the requested date. var currentSummary = await cache.GetCountersSummary(audit.ComponentId, audit.Date); var component = new InfrastructureComponentSummaryWithHistory { Date = date, Component = new InfrastructureComponent(audit.ComponentId) { Category = GetCategory(audit.ComponentId), Name = audit.ComponentName, }, Current = currentSummary, ScoreHistory = componentHistory.OrderBy(i => i.RecordedAt).ToArray(), }; components.Add(component); } return(new InfrastructureOverview { Overall = overall, Components = components.ToArray(), }); }