1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

[PM-23580] Security Task Metrics (#6164)

* add metrics endpoint for an organization to return completed and total security tasks

* refactor metrics fetch to use sql sproc for efficiency rather than having to pull all security task data

* add separate response model for security task metrics endpoint

* Pascal Case to match existing implementations

* refactor org to organization for consistency with other methods

* alter security task endpoint:
- remove "count" from variable naming
- update sproc naming

* remove enablement check

* replace orgId with organizationId
This commit is contained in:
Nick Krantz
2025-08-13 08:23:22 -05:00
committed by GitHub
parent 9022ad2360
commit f88baba66b
12 changed files with 254 additions and 1 deletions

View File

@@ -266,4 +266,83 @@ public class SecurityTaskRepositoryTests
Assert.Equal(2, taskIds.Count);
}
[DatabaseTheory, DatabaseData]
public async Task GetTaskMetricsAsync(
IOrganizationRepository organizationRepository,
ICipherRepository cipherRepository,
ISecurityTaskRepository securityTaskRepository)
{
var organization = await organizationRepository.CreateAsync(new Organization
{
Name = "Test Org",
PlanType = PlanType.EnterpriseAnnually,
Plan = "Test Plan",
BillingEmail = ""
});
var cipher1 = new Cipher { Type = CipherType.Login, OrganizationId = organization.Id, Data = "", };
await cipherRepository.CreateAsync(cipher1);
var cipher2 = new Cipher { Type = CipherType.Login, OrganizationId = organization.Id, Data = "", };
await cipherRepository.CreateAsync(cipher2);
var tasks = new List<SecurityTask>
{
new()
{
OrganizationId = organization.Id,
CipherId = cipher1.Id,
Status = SecurityTaskStatus.Pending,
Type = SecurityTaskType.UpdateAtRiskCredential,
},
new()
{
OrganizationId = organization.Id,
CipherId = cipher1.Id,
Status = SecurityTaskStatus.Completed,
Type = SecurityTaskType.UpdateAtRiskCredential,
},
new()
{
OrganizationId = organization.Id,
CipherId = cipher2.Id,
Status = SecurityTaskStatus.Completed,
Type = SecurityTaskType.UpdateAtRiskCredential,
},
new()
{
OrganizationId = organization.Id,
CipherId = cipher2.Id,
Status = SecurityTaskStatus.Pending,
Type = SecurityTaskType.UpdateAtRiskCredential,
}
};
await securityTaskRepository.CreateManyAsync(tasks);
var metrics = await securityTaskRepository.GetTaskMetricsAsync(organization.Id);
Assert.Equal(2, metrics.CompletedTasks);
Assert.Equal(4, metrics.TotalTasks);
}
[DatabaseTheory, DatabaseData]
public async Task GetZeroTaskMetricsAsync(
IOrganizationRepository organizationRepository,
ISecurityTaskRepository securityTaskRepository)
{
var organization = await organizationRepository.CreateAsync(new Organization
{
Name = "Test Org",
PlanType = PlanType.EnterpriseAnnually,
Plan = "Test Plan",
BillingEmail = ""
});
var metrics = await securityTaskRepository.GetTaskMetricsAsync(organization.Id);
Assert.Equal(0, metrics.CompletedTasks);
Assert.Equal(0, metrics.TotalTasks);
}
}