Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,58 @@ public async Task GetCapabilityCompliance_TagWithEmptyValue_TreatedAsMissing()
var costCentreItem = tagsCategory.Items.First(i => i.Name == "dfds.cost.centre");
Assert.Equal("missing", costCentreItem.Status);
}

[Fact]
public async Task GetRogueCapabilitiesCompliance_FiltersCapabilitiesWithoutCostCentre()
{
var withCostCentre = A
.Capability.WithId(CapabilityId.CreateFrom("cost-centre-cap"))
.WithJsonMetadata(AllTagsPresent)
.Build();
var missingCostCentre = A
.Capability.WithId(CapabilityId.CreateFrom("missing-cost-centre-cap"))
.WithJsonMetadata("""{"dfds.businessCapability": "Platform"}""")
.Build();
var emptyCostCentre = A
.Capability.WithId(CapabilityId.CreateFrom("empty-cost-centre-cap"))
.WithJsonMetadata("""{"dfds.cost.centre": ""}""")
.Build();

var repo = new Mock<ICapabilityRepository>();
repo.Setup(r => r.GetAllActive()).ReturnsAsync(new[] { withCostCentre, missingCostCentre, emptyCostCentre });

var service = A.ComplianceApplicationService.WithCapabilityRepository(repo.Object).Build();

var result = await service.GetRogueCapabilitiesCompliance();

Assert.Equal("rogue", result.CostCentre);
Assert.Equal(2, result.TotalCapabilities);
}

[Fact]
public async Task GetRogueCapabilitiesComplianceDetails_MatchesAggregateCounts()
{
var rogueCap = A
.Capability.WithId(CapabilityId.CreateFrom("rogue-cap"))
.WithJsonMetadata("""{"dfds.businessCapability": "Platform"}""")
.Build();
var nonRogueCap = A
.Capability.WithId(CapabilityId.CreateFrom("non-rogue-cap"))
.WithJsonMetadata(AllTagsPresent)
.Build();

var repo = new Mock<ICapabilityRepository>();
repo.Setup(r => r.GetAllActive()).ReturnsAsync(new[] { rogueCap, nonRogueCap });

var service = A.ComplianceApplicationService.WithCapabilityRepository(repo.Object).Build();

var details = await service.GetRogueCapabilitiesComplianceDetails();
var aggregate = await service.GetRogueCapabilitiesCompliance();

Assert.Equal("rogue", details.CostCentre);
Assert.Single(details.Capabilities);
Assert.Equal(aggregate.TotalCapabilities, details.TotalCapabilities);
Assert.Equal(aggregate.CompliantCount, details.CompliantCount);
Assert.Equal(aggregate.NonCompliantCount, details.NonCompliantCount);
}
}
55 changes: 41 additions & 14 deletions src/SelfService/Application/ComplianceApplicationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class ComplianceApplicationService : IComplianceApplicationService
private readonly RequirementsDbContext _requirementsDbContext;

private static readonly string[] PlaceholderCategories = Array.Empty<string>();
private const string RogueCostCentreName = "rogue";

private static readonly string[] Categories =
{
Expand Down Expand Up @@ -79,25 +80,39 @@ private async Task<bool> HasKubernetesContext(CapabilityId capabilityId)
public async Task<CostCentreComplianceResult> GetCostCentreCompliance(string costCentre)
{
var details = await GetCostCentreComplianceDetails(costCentre);
return new CostCentreComplianceResult
{
CostCentre = details.CostCentre,
TotalCapabilities = details.TotalCapabilities,
CompliantCount = details.CompliantCount,
NonCompliantCount = details.NonCompliantCount,
Categories = details.Categories,
};
return ToCostCentreComplianceResult(details);
}

public async Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDetails(string costCentre)
{
return await BuildComplianceDetailsForCapabilities(
costCentre,
c => string.Equals(ExtractCostCentre(c.JsonMetadata), costCentre, StringComparison.OrdinalIgnoreCase)
);
}

public async Task<CostCentreComplianceResult> GetRogueCapabilitiesCompliance()
{
var details = await GetRogueCapabilitiesComplianceDetails();
return ToCostCentreComplianceResult(details);
}

public async Task<CostCentreComplianceDetailsResult> GetRogueCapabilitiesComplianceDetails()
{
return await BuildComplianceDetailsForCapabilities(
RogueCostCentreName,
c => string.IsNullOrWhiteSpace(ExtractCostCentre(c.JsonMetadata))
);
}

private async Task<CostCentreComplianceDetailsResult> BuildComplianceDetailsForCapabilities(
string costCentreLabel,
Func<Capability, bool> filter
)
{
var activeCapabilities = await _capabilityRepository.GetAllActive();

var matchingCapabilities = activeCapabilities
.Where(c =>
string.Equals(ExtractCostCentre(c.JsonMetadata), costCentre, StringComparison.OrdinalIgnoreCase)
)
.ToList();
var matchingCapabilities = activeCapabilities.Where(filter).ToList();

var capabilityIds = matchingCapabilities.Select(c => c.Id.ToString()).ToList();
var allMetrics = await _requirementsDbContext
Expand Down Expand Up @@ -161,7 +176,7 @@ public async Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDeta

return new CostCentreComplianceDetailsResult
{
CostCentre = costCentre,
CostCentre = costCentreLabel,
TotalCapabilities = matchingCapabilities.Count,
CompliantCount = capabilityResults.Count(r => r.OverallStatus == ComplianceStatus.Compliant),
NonCompliantCount = capabilityResults.Count(r => r.OverallStatus == ComplianceStatus.NonCompliant),
Expand All @@ -171,6 +186,18 @@ public async Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDeta
};
}

private static CostCentreComplianceResult ToCostCentreComplianceResult(CostCentreComplianceDetailsResult details)
{
return new CostCentreComplianceResult
{
CostCentre = details.CostCentre,
TotalCapabilities = details.TotalCapabilities,
CompliantCount = details.CompliantCount,
NonCompliantCount = details.NonCompliantCount,
Categories = details.Categories,
};
}

private static ComplianceCategoryResult CheckTagCompliance(string? jsonMetadata)
{
var evaluation = TagComplianceEvaluator.Evaluate(jsonMetadata);
Expand Down
2 changes: 2 additions & 0 deletions src/SelfService/Application/IComplianceApplicationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ public interface IComplianceApplicationService
Task<CapabilityComplianceResult> GetCapabilityCompliance(CapabilityId capabilityId);
Task<CostCentreComplianceResult> GetCostCentreCompliance(string costCentre);
Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDetails(string costCentre);
Task<CostCentreComplianceResult> GetRogueCapabilitiesCompliance();
Task<CostCentreComplianceDetailsResult> GetRogueCapabilitiesComplianceDetails();
}
55 changes: 41 additions & 14 deletions src/SelfService/Application/StubComplianceApplicationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class StubComplianceApplicationService : IComplianceApplicationService
private readonly IAwsAccountRepository _awsAccountRepository;

private static readonly string[] PlaceholderCategories = Array.Empty<string>();
private const string RogueCostCentreName = "rogue";

public StubComplianceApplicationService(
ICapabilityRepository capabilityRepository,
Expand Down Expand Up @@ -108,25 +109,39 @@ public async Task<CapabilityComplianceResult> GetCapabilityCompliance(Capability
public async Task<CostCentreComplianceResult> GetCostCentreCompliance(string costCentre)
{
var details = await GetCostCentreComplianceDetails(costCentre);
return new CostCentreComplianceResult
{
CostCentre = details.CostCentre,
TotalCapabilities = details.TotalCapabilities,
CompliantCount = details.CompliantCount,
NonCompliantCount = details.NonCompliantCount,
Categories = details.Categories,
};
return ToCostCentreComplianceResult(details);
}

public async Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDetails(string costCentre)
{
return await BuildComplianceDetailsForCapabilities(
costCentre,
c => string.Equals(ExtractCostCentre(c.JsonMetadata), costCentre, StringComparison.OrdinalIgnoreCase)
);
}

public async Task<CostCentreComplianceResult> GetRogueCapabilitiesCompliance()
{
var details = await GetRogueCapabilitiesComplianceDetails();
return ToCostCentreComplianceResult(details);
}

public async Task<CostCentreComplianceDetailsResult> GetRogueCapabilitiesComplianceDetails()
{
return await BuildComplianceDetailsForCapabilities(
RogueCostCentreName,
c => string.IsNullOrWhiteSpace(ExtractCostCentre(c.JsonMetadata))
);
}

private async Task<CostCentreComplianceDetailsResult> BuildComplianceDetailsForCapabilities(
string costCentreLabel,
Func<Capability, bool> filter
)
{
var activeCapabilities = await _capabilityRepository.GetAllActive();

var matchingCapabilities = activeCapabilities
.Where(c =>
string.Equals(ExtractCostCentre(c.JsonMetadata), costCentre, StringComparison.OrdinalIgnoreCase)
)
.ToList();
var matchingCapabilities = activeCapabilities.Where(filter).ToList();

var awsAccounts = await _awsAccountRepository.GetByCapabilityIds(matchingCapabilities.Select(c => c.Id));
var k8sCapabilityIds = awsAccounts
Expand Down Expand Up @@ -216,7 +231,7 @@ public async Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDeta

return new CostCentreComplianceDetailsResult
{
CostCentre = costCentre,
CostCentre = costCentreLabel,
TotalCapabilities = matchingCapabilities.Count,
CompliantCount = capabilityResults.Count(r => r.OverallStatus == ComplianceStatus.Compliant),
NonCompliantCount = capabilityResults.Count(r => r.OverallStatus == ComplianceStatus.NonCompliant),
Expand All @@ -226,6 +241,18 @@ public async Task<CostCentreComplianceDetailsResult> GetCostCentreComplianceDeta
};
}

private static CostCentreComplianceResult ToCostCentreComplianceResult(CostCentreComplianceDetailsResult details)
{
return new CostCentreComplianceResult
{
CostCentre = details.CostCentre,
TotalCapabilities = details.TotalCapabilities,
CompliantCount = details.CompliantCount,
NonCompliantCount = details.NonCompliantCount,
Categories = details.Categories,
};
}

private async Task<bool> HasKubernetesContext(CapabilityId capabilityId)
{
var awsAccount = await _awsAccountRepository.FindBy(capabilityId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,18 @@ public async Task<IActionResult> GetCostCentreComplianceDetails([FromRoute] stri
var result = await _complianceService.GetCostCentreComplianceDetails(costCentre);
return Ok(CostCentreComplianceDetailsApiResource.From(result));
}

[HttpGet("rogue-capabilities")]
public async Task<IActionResult> GetRogueCapabilitiesCompliance()
{
var result = await _complianceService.GetRogueCapabilitiesCompliance();
return Ok(CostCentreComplianceApiResource.From(result));
}

[HttpGet("rogue-capabilities/details")]
public async Task<IActionResult> GetRogueCapabilitiesComplianceDetails()
{
var result = await _complianceService.GetRogueCapabilitiesComplianceDetails();
return Ok(CostCentreComplianceDetailsApiResource.From(result));
}
}
Loading