mirror of
https://github.com/bitwarden/server.git
synced 2025-06-25 13:18:48 -05:00
[PM-20577] OrganizationReport endpoints (#5986)
* PM-20574 fixing namespaces on reporting work that got moved over from tools * PM-20574 adding tables, stored procedures, and migration files * PM-20574 adding dapper and ef repos and migrations * PM-20574 changing table and repo names as requested * PM-20574 updating sql scripts to new names * PM-20574 updating sql scripts * PM-20574 updating migration script for org delete by id * PM-20574 adding mysql migration * PM-20574 updating sql migration to fix database test * PM-20574 fixing migration script * PM-20574 fixing migration script * PM-20574 fixing table scripts * PM-20574 fixing table scripts * PM-20574 fixing migration script formatting * PM-20574 fixing syntax in migration script * PM-20574 fixing file names and extensions * PM-20574 fixing sql file * PM-20574 fixing sql * PM-20574 fixing directory for entities and removing scripts from other databases * PM-20574 generating new migration scripts * PM-20574 fixed reference to a stored proc * PM-20574 adding index in scripts and missing table * PM-20574 fixing merge conflicts * PM-20574 set OUTPUT param for Id property in create and update proc * PM-20574 add CreateDate to the update proc * PM-20574 amend update proc for OrganizationApplication by adding createDate * PM-20576 Created OrganizationReportRepo and unit tests * PM-20576 Commands and Query for OrganizationReport * PM-20576 added additional unit tests to fix CodeCoverage report * PM-20574 formatted sql and updated as per PR comments * PM-20574 updated script to fix build error * PM-20574 fixed inconsistency in db script * PM-20577 organization-reports endpoints * PM-20574 removed revisionDate, update procedures and used views * PM-20574 removed RevisionDate from designer files * PM-20574 removed revisionDate column that was missed previously * PM-20574 added revision date back into the mix * PM-20574 updated database script to fix build error * PM-20574 fixed a procedure issue * PM-20574 fix dB build error * PM-020574 fixed additional PR comments - files cleaned up * PM-20574 updated procedure was inconsistent * PM-20576 added logs and updated errors as per PR comments * PM-20576 fixed a build error * PM-20576 removed RevisionDate from Repo and tests * PM-20576 added dependency * PM-20576 removed unwanted line from csproj file --------- Co-authored-by: Graham Walker <gwalker@bitwarden.com> Co-authored-by: Tom <144813356+ttalty@users.noreply.github.com>
This commit is contained in:
parent
51e93c7323
commit
74964bf170
@ -23,6 +23,9 @@ public class ReportsController : Controller
|
|||||||
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
|
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
|
||||||
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
|
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
|
||||||
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
|
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
|
||||||
|
private readonly IAddOrganizationReportCommand _addOrganizationReportCommand;
|
||||||
|
private readonly IDropOrganizationReportCommand _dropOrganizationReportCommand;
|
||||||
|
private readonly IGetOrganizationReportQuery _getOrganizationReportQuery;
|
||||||
|
|
||||||
public ReportsController(
|
public ReportsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
@ -30,7 +33,10 @@ public class ReportsController : Controller
|
|||||||
IRiskInsightsReportQuery riskInsightsReportQuery,
|
IRiskInsightsReportQuery riskInsightsReportQuery,
|
||||||
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
|
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
|
||||||
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
|
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
|
||||||
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand
|
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand,
|
||||||
|
IGetOrganizationReportQuery getOrganizationReportQuery,
|
||||||
|
IAddOrganizationReportCommand addOrganizationReportCommand,
|
||||||
|
IDropOrganizationReportCommand dropOrganizationReportCommand
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
@ -39,6 +45,9 @@ public class ReportsController : Controller
|
|||||||
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
|
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
|
||||||
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
|
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
|
||||||
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
|
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
|
||||||
|
_getOrganizationReportQuery = getOrganizationReportQuery;
|
||||||
|
_addOrganizationReportCommand = addOrganizationReportCommand;
|
||||||
|
_dropOrganizationReportCommand = dropOrganizationReportCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -204,4 +213,72 @@ public class ReportsController : Controller
|
|||||||
|
|
||||||
await _dropPwdHealthReportAppCommand.DropPasswordHealthReportApplicationAsync(request);
|
await _dropPwdHealthReportAppCommand.DropPasswordHealthReportApplicationAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new organization report
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">A single instance of AddOrganizationReportRequest</param>
|
||||||
|
/// <returns>A single instance of OrganizationReport</returns>
|
||||||
|
/// <exception cref="NotFoundException">If user does not have access to the organization</exception>
|
||||||
|
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
|
||||||
|
[HttpPost("organization-reports")]
|
||||||
|
public async Task<OrganizationReport> AddOrganizationReport([FromBody] AddOrganizationReportRequest request)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.AccessReports(request.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
return await _addOrganizationReportCommand.AddOrganizationReportAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drops organization reports for an organization
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">A single instance of DropOrganizationReportRequest</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotFoundException">If user does not have access to the organization</exception>
|
||||||
|
/// <exception cref="BadRequestException">If the organization does not have any records</exception>
|
||||||
|
[HttpDelete("organization-reports")]
|
||||||
|
public async Task DropOrganizationReport([FromBody] DropOrganizationReportRequest request)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.AccessReports(request.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
await _dropOrganizationReportCommand.DropOrganizationReportAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets organization reports for an organization
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orgId">A valid Organization Id</param>
|
||||||
|
/// <returns>An Enumerable of OrganizationReport</returns>
|
||||||
|
/// <exception cref="NotFoundException">If user does not have access to the organization</exception>
|
||||||
|
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
|
||||||
|
[HttpGet("organization-reports/{orgId}")]
|
||||||
|
public async Task<IEnumerable<OrganizationReport>> GetOrganizationReports(Guid orgId)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.AccessReports(orgId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
return await _getOrganizationReportQuery.GetOrganizationReportAsync(orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the latest organization report for an organization
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orgId">A valid Organization Id</param>
|
||||||
|
/// <returns>A single instance of OrganizationReport</returns>
|
||||||
|
/// <exception cref="NotFoundException">If user does not have access to the organization</exception>
|
||||||
|
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
|
||||||
|
[HttpGet("organization-reports/latest/{orgId}")]
|
||||||
|
public async Task<OrganizationReport> GetLatestOrganizationReport(Guid orgId)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.AccessReports(orgId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
return await _getOrganizationReportQuery.GetLatestOrganizationReportAsync(orgId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,15 @@ public class DropOrganizationReportCommand : IDropOrganizationReportCommand
|
|||||||
throw new BadRequestException("No data found.");
|
throw new BadRequestException("No data found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Where(_ => request.OrganizationReportIds.Contains(_.Id)).ToList().ForEach(async _ =>
|
data
|
||||||
{
|
.Where(_ => request.OrganizationReportIds.Contains(_.Id))
|
||||||
_logger.LogInformation("Dropping organization report {organizationReportId} for organization {organizationId}",
|
.ToList()
|
||||||
_.Id, request.OrganizationId);
|
.ForEach(async reportId =>
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Dropping organization report {organizationReportId} for organization {organizationId}",
|
||||||
|
reportId, request.OrganizationId);
|
||||||
|
|
||||||
await _organizationReportRepo.DeleteAsync(_);
|
await _organizationReportRepo.DeleteAsync(reportId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,4 +142,142 @@ public class ReportsControllerTests
|
|||||||
_.OrganizationId == request.OrganizationId &&
|
_.OrganizationId == request.OrganizationId &&
|
||||||
_.PasswordHealthReportApplicationIds == request.PasswordHealthReportApplicationIds));
|
_.PasswordHealthReportApplicationIds == request.PasswordHealthReportApplicationIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task AddOrganizationReportAsync_withAccess_success(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var request = new AddOrganizationReportRequest
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.NewGuid(),
|
||||||
|
ReportData = "Report Data",
|
||||||
|
Date = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
await sutProvider.Sut.AddOrganizationReport(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IAddOrganizationReportCommand>()
|
||||||
|
.Received(1)
|
||||||
|
.AddOrganizationReportAsync(Arg.Is<AddOrganizationReportRequest>(_ =>
|
||||||
|
_.OrganizationId == request.OrganizationId &&
|
||||||
|
_.ReportData == request.ReportData &&
|
||||||
|
_.Date == request.Date));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task AddOrganizationReportAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
|
||||||
|
// Act
|
||||||
|
var request = new AddOrganizationReportRequest
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.NewGuid(),
|
||||||
|
ReportData = "Report Data",
|
||||||
|
Date = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||||
|
await sutProvider.Sut.AddOrganizationReport(request));
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IAddOrganizationReportCommand>()
|
||||||
|
.Received(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task DropOrganizationReportAsync_withAccess_success(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
|
||||||
|
// Act
|
||||||
|
var request = new DropOrganizationReportRequest
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.NewGuid(),
|
||||||
|
OrganizationReportIds = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() }
|
||||||
|
};
|
||||||
|
await sutProvider.Sut.DropOrganizationReport(request);
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IDropOrganizationReportCommand>()
|
||||||
|
.Received(1)
|
||||||
|
.DropOrganizationReportAsync(Arg.Is<DropOrganizationReportRequest>(_ =>
|
||||||
|
_.OrganizationId == request.OrganizationId &&
|
||||||
|
_.OrganizationReportIds.SequenceEqual(request.OrganizationReportIds)));
|
||||||
|
}
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task DropOrganizationReportAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
|
||||||
|
// Act
|
||||||
|
var request = new DropOrganizationReportRequest
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.NewGuid(),
|
||||||
|
OrganizationReportIds = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() }
|
||||||
|
};
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||||
|
await sutProvider.Sut.DropOrganizationReport(request));
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IDropOrganizationReportCommand>()
|
||||||
|
.Received(0);
|
||||||
|
}
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetOrganizationReportAsync_withAccess_success(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
|
||||||
|
// Act
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var result = await sutProvider.Sut.GetOrganizationReports(orgId);
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IGetOrganizationReportQuery>()
|
||||||
|
.Received(1)
|
||||||
|
.GetOrganizationReportAsync(Arg.Is<Guid>(_ => _ == orgId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetOrganizationReportAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
|
||||||
|
// Act
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetOrganizationReports(orgId));
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IGetOrganizationReportQuery>()
|
||||||
|
.Received(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetLastestOrganizationReportAsync_withAccess_success(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var result = await sutProvider.Sut.GetLatestOrganizationReport(orgId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IGetOrganizationReportQuery>()
|
||||||
|
.Received(1)
|
||||||
|
.GetLatestOrganizationReportAsync(Arg.Is<Guid>(_ => _ == orgId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetLastestOrganizationReportAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetLatestOrganizationReport(orgId));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IGetOrganizationReportQuery>()
|
||||||
|
.Received(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user