diff --git a/src/SelfService/Application/ComplianceApplicationService.cs b/src/SelfService/Application/ComplianceApplicationService.cs index 15a9a4fd..374f1179 100644 --- a/src/SelfService/Application/ComplianceApplicationService.cs +++ b/src/SelfService/Application/ComplianceApplicationService.cs @@ -114,6 +114,18 @@ public async Task GetRogueCapabilitiesComplia ); } + public async Task GetComplianceSummary() + { + var all = await BuildComplianceDetailsForCapabilities("all", _ => true); + return new ComplianceSummaryResult + { + TotalCapabilities = all.TotalCapabilities, + FullyCompliantCapabilities = all.CompliantCount, + NonCompliantCapabilities = all.NonCompliantCount, + UnknownCapabilities = all.UnknownCount, + }; + } + public async Task GetRequirementsCompliance() { var allCapabilitiesDetails = await BuildComplianceDetailsForCapabilities("all", _ => true); @@ -236,15 +248,30 @@ private static RequirementComplianceSummaryResult BuildRequirementSummary( List capabilities ) { - var categories = capabilities - .Select(capability => - capability.Categories.FirstOrDefault(category => category.CategoryName == definition.CategoryName) - ) - .Where(category => category != null) - .Select(category => category!) + var matched = capabilities + .Select(cap => new + { + cap, + category = cap.Categories.FirstOrDefault(c => c.CategoryName == definition.CategoryName), + }) + .Where(x => x.category != null) + .Select(x => new { x.cap, category = x.category! }) .ToList(); - var metadataSource = categories.FirstOrDefault(); + var metadataSource = matched.Select(x => x.category).FirstOrDefault(); + + var byCostCentre = matched + .GroupBy(x => ExtractCostCentre(x.cap.JsonMetadata)) + .OrderBy(g => g.Key) + .Select(g => new RequirementByCostCentreResult + { + CostCentre = g.Key, + TotalCapabilities = g.Count(), + CompliantCount = g.Count(x => x.category.Status == ComplianceStatus.Compliant), + NonCompliantCount = g.Count(x => x.category.Status == ComplianceStatus.NonCompliant), + UnknownCount = g.Count(x => x.category.Status == ComplianceStatus.Unknown), + }) + .ToList(); return new RequirementComplianceSummaryResult { @@ -253,10 +280,11 @@ List capabilities DisplayName = metadataSource?.DisplayName ?? definition.CategoryName, Description = metadataSource?.Description, HelpUrl = metadataSource?.HelpUrl, - TotalCapabilities = categories.Count, - CompliantCount = categories.Count(category => category.Status == ComplianceStatus.Compliant), - NonCompliantCount = categories.Count(category => category.Status == ComplianceStatus.NonCompliant), - UnknownCount = categories.Count(category => category.Status == ComplianceStatus.Unknown), + TotalCapabilities = matched.Count, + CompliantCount = matched.Count(x => x.category.Status == ComplianceStatus.Compliant), + NonCompliantCount = matched.Count(x => x.category.Status == ComplianceStatus.NonCompliant), + UnknownCount = matched.Count(x => x.category.Status == ComplianceStatus.Unknown), + ByCostCentre = byCostCentre, }; } diff --git a/src/SelfService/Application/ComplianceModels.cs b/src/SelfService/Application/ComplianceModels.cs index ab47b031..f7d0f59b 100644 --- a/src/SelfService/Application/ComplianceModels.cs +++ b/src/SelfService/Application/ComplianceModels.cs @@ -69,11 +69,28 @@ public class CostCentreCapabilityComplianceResult public List Categories { get; set; } = new(); } +public class ComplianceSummaryResult +{ + public int TotalCapabilities { get; set; } + public int FullyCompliantCapabilities { get; set; } + public int NonCompliantCapabilities { get; set; } + public int UnknownCapabilities { get; set; } +} + public class RequirementsComplianceResult { public List Items { get; set; } = new(); } +public class RequirementByCostCentreResult +{ + public string? CostCentre { get; set; } + public int TotalCapabilities { get; set; } + public int CompliantCount { get; set; } + public int NonCompliantCount { get; set; } + public int UnknownCount { get; set; } +} + public class RequirementComplianceSummaryResult { public string RequirementId { get; set; } = null!; @@ -85,6 +102,7 @@ public class RequirementComplianceSummaryResult public int CompliantCount { get; set; } public int NonCompliantCount { get; set; } public int UnknownCount { get; set; } + public List ByCostCentre { get; set; } = new(); } public class RequirementComplianceDetailsResult : RequirementComplianceSummaryResult diff --git a/src/SelfService/Application/IComplianceApplicationService.cs b/src/SelfService/Application/IComplianceApplicationService.cs index e5bd6692..b29f5ef0 100644 --- a/src/SelfService/Application/IComplianceApplicationService.cs +++ b/src/SelfService/Application/IComplianceApplicationService.cs @@ -4,6 +4,7 @@ namespace SelfService.Application; public interface IComplianceApplicationService { + Task GetComplianceSummary(); Task GetCapabilityCompliance(CapabilityId capabilityId); Task GetCostCentreCompliance(string costCentre); Task GetCostCentreComplianceDetails(string costCentre); diff --git a/src/SelfService/Application/StubComplianceApplicationService.cs b/src/SelfService/Application/StubComplianceApplicationService.cs index c6e68f47..a90d9066 100644 --- a/src/SelfService/Application/StubComplianceApplicationService.cs +++ b/src/SelfService/Application/StubComplianceApplicationService.cs @@ -143,6 +143,18 @@ public async Task GetRogueCapabilitiesComplia ); } + public async Task GetComplianceSummary() + { + var all = await BuildComplianceDetailsForCapabilities("all", _ => true); + return new ComplianceSummaryResult + { + TotalCapabilities = all.TotalCapabilities, + FullyCompliantCapabilities = all.CompliantCount, + NonCompliantCapabilities = all.NonCompliantCount, + UnknownCapabilities = all.UnknownCount, + }; + } + public async Task GetRequirementsCompliance() { var allCapabilitiesDetails = await BuildComplianceDetailsForCapabilities("all", _ => true); @@ -291,15 +303,30 @@ private static RequirementComplianceSummaryResult BuildRequirementSummary( List capabilities ) { - var categories = capabilities - .Select(capability => - capability.Categories.FirstOrDefault(category => category.CategoryName == definition.CategoryName) - ) - .Where(category => category != null) - .Select(category => category!) + var matched = capabilities + .Select(cap => new + { + cap, + category = cap.Categories.FirstOrDefault(c => c.CategoryName == definition.CategoryName), + }) + .Where(x => x.category != null) + .Select(x => new { x.cap, category = x.category! }) .ToList(); - var metadataSource = categories.FirstOrDefault(); + var metadataSource = matched.Select(x => x.category).FirstOrDefault(); + + var byCostCentre = matched + .GroupBy(x => ExtractCostCentre(x.cap.JsonMetadata)) + .OrderBy(g => g.Key) + .Select(g => new RequirementByCostCentreResult + { + CostCentre = g.Key, + TotalCapabilities = g.Count(), + CompliantCount = g.Count(x => x.category.Status == ComplianceStatus.Compliant), + NonCompliantCount = g.Count(x => x.category.Status == ComplianceStatus.NonCompliant), + UnknownCount = g.Count(x => x.category.Status == ComplianceStatus.Unknown), + }) + .ToList(); return new RequirementComplianceSummaryResult { @@ -308,10 +335,11 @@ List capabilities DisplayName = metadataSource?.DisplayName ?? definition.CategoryName, Description = metadataSource?.Description, HelpUrl = metadataSource?.HelpUrl, - TotalCapabilities = categories.Count, - CompliantCount = categories.Count(category => category.Status == ComplianceStatus.Compliant), - NonCompliantCount = categories.Count(category => category.Status == ComplianceStatus.NonCompliant), - UnknownCount = categories.Count(category => category.Status == ComplianceStatus.Unknown), + TotalCapabilities = matched.Count, + CompliantCount = matched.Count(x => x.category.Status == ComplianceStatus.Compliant), + NonCompliantCount = matched.Count(x => x.category.Status == ComplianceStatus.NonCompliant), + UnknownCount = matched.Count(x => x.category.Status == ComplianceStatus.Unknown), + ByCostCentre = byCostCentre, }; } diff --git a/src/SelfService/Infrastructure/Api/Compliance/ComplianceApiResources.cs b/src/SelfService/Infrastructure/Api/Compliance/ComplianceApiResources.cs index 1be74af7..db52538f 100644 --- a/src/SelfService/Infrastructure/Api/Compliance/ComplianceApiResources.cs +++ b/src/SelfService/Infrastructure/Api/Compliance/ComplianceApiResources.cs @@ -2,6 +2,25 @@ namespace SelfService.Infrastructure.Api.Compliance; +public class ComplianceSummaryApiResource +{ + public int TotalCapabilities { get; set; } + public int FullyCompliantCapabilities { get; set; } + public int NonCompliantCapabilities { get; set; } + public int UnknownCapabilities { get; set; } + + public static ComplianceSummaryApiResource From(ComplianceSummaryResult result) + { + return new ComplianceSummaryApiResource + { + TotalCapabilities = result.TotalCapabilities, + FullyCompliantCapabilities = result.FullyCompliantCapabilities, + NonCompliantCapabilities = result.NonCompliantCapabilities, + UnknownCapabilities = result.UnknownCapabilities, + }; + } +} + public class CapabilityComplianceApiResource { public string CapabilityId { get; set; } = null!; @@ -160,6 +179,27 @@ public static RequirementsComplianceApiResource From(RequirementsComplianceResul } } +public class RequirementByCostCentreApiResource +{ + public string? CostCentre { get; set; } + public int TotalCapabilities { get; set; } + public int CompliantCount { get; set; } + public int NonCompliantCount { get; set; } + public int UnknownCount { get; set; } + + public static RequirementByCostCentreApiResource From(RequirementByCostCentreResult result) + { + return new RequirementByCostCentreApiResource + { + CostCentre = result.CostCentre, + TotalCapabilities = result.TotalCapabilities, + CompliantCount = result.CompliantCount, + NonCompliantCount = result.NonCompliantCount, + UnknownCount = result.UnknownCount, + }; + } +} + public class RequirementComplianceSummaryApiResource { public string RequirementId { get; set; } = null!; @@ -171,6 +211,7 @@ public class RequirementComplianceSummaryApiResource public int CompliantCount { get; set; } public int NonCompliantCount { get; set; } public int UnknownCount { get; set; } + public List ByCostCentre { get; set; } = new(); public static RequirementComplianceSummaryApiResource From(RequirementComplianceSummaryResult result) { @@ -185,6 +226,7 @@ public static RequirementComplianceSummaryApiResource From(RequirementCompliance CompliantCount = result.CompliantCount, NonCompliantCount = result.NonCompliantCount, UnknownCount = result.UnknownCount, + ByCostCentre = result.ByCostCentre.Select(RequirementByCostCentreApiResource.From).ToList(), }; } } diff --git a/src/SelfService/Infrastructure/Api/Compliance/ComplianceController.cs b/src/SelfService/Infrastructure/Api/Compliance/ComplianceController.cs index 2669e546..aba3e384 100644 --- a/src/SelfService/Infrastructure/Api/Compliance/ComplianceController.cs +++ b/src/SelfService/Infrastructure/Api/Compliance/ComplianceController.cs @@ -17,6 +17,13 @@ public ComplianceController(IComplianceApplicationService complianceService) _complianceService = complianceService; } + [HttpGet("summary")] + public async Task GetComplianceSummary() + { + var result = await _complianceService.GetComplianceSummary(); + return Ok(ComplianceSummaryApiResource.From(result)); + } + [HttpGet("capabilities/{id}")] public async Task GetCapabilityCompliance([FromRoute] string id) {