mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
Organization report tables, repos, services, and endpoints (#6158)
* PM-23754 initial commit * pm-23754 fixing controller tests * pm-23754 adding commands and queries * pm-23754 adding endpoints, command/queries, repositories, and sql migrations * pm-23754 add new sql scripts * PM-23754 adding sql scripts * pm-23754 * PM-23754 fixing migration script * PM-23754 fixing migration script again * PM-23754 fixing migration script validation * PM-23754 fixing db validation script issue * PM-23754 fixing endpoint and db validation * PM-23754 fixing unit tests * PM-23754 fixing implementation based on comments and tests * PM-23754 updating logging statements * PM-23754 making changes based on PR comments. * updating migration scripts * removing old migration files * update code based testing for whole data object for OrganizationReport and add a stored procedure. * updating services, unit tests, repository tests * fixing unit tests * fixing migration script * fixing migration script again * fixing migration script * another fix * fixing sql file, updating controller to account for different orgIds in the url and body. * updating error message in controllers without a body * making a change to the command * Refactor ReportsController by removing organization reports The IDropOrganizationReportCommand is no longer needed * will code based on PR comments. * fixing unit test * fixing migration script based on last changes. * adding another check in endpoint and adding unit tests * fixing route parameter. * PM-23754 updating data fields to return just the column * PM-23754 fixing repository method signatures * PM-23754 making change to orgId parameter through out code to align with api naming --------- Co-authored-by: Tom <144813356+ttalty@users.noreply.github.com>
This commit is contained in:
297
src/Api/Dirt/Controllers/OrganizationReportsController.cs
Normal file
297
src/Api/Dirt/Controllers/OrganizationReportsController.cs
Normal file
@@ -0,0 +1,297 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Dirt.Controllers;
|
||||
|
||||
[Route("reports/organizations")]
|
||||
[Authorize("Application")]
|
||||
public class OrganizationReportsController : Controller
|
||||
{
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IGetOrganizationReportQuery _getOrganizationReportQuery;
|
||||
private readonly IAddOrganizationReportCommand _addOrganizationReportCommand;
|
||||
private readonly IUpdateOrganizationReportCommand _updateOrganizationReportCommand;
|
||||
private readonly IUpdateOrganizationReportSummaryCommand _updateOrganizationReportSummaryCommand;
|
||||
private readonly IGetOrganizationReportSummaryDataQuery _getOrganizationReportSummaryDataQuery;
|
||||
private readonly IGetOrganizationReportSummaryDataByDateRangeQuery _getOrganizationReportSummaryDataByDateRangeQuery;
|
||||
private readonly IGetOrganizationReportDataQuery _getOrganizationReportDataQuery;
|
||||
private readonly IUpdateOrganizationReportDataCommand _updateOrganizationReportDataCommand;
|
||||
private readonly IGetOrganizationReportApplicationDataQuery _getOrganizationReportApplicationDataQuery;
|
||||
private readonly IUpdateOrganizationReportApplicationDataCommand _updateOrganizationReportApplicationDataCommand;
|
||||
|
||||
public OrganizationReportsController(
|
||||
ICurrentContext currentContext,
|
||||
IGetOrganizationReportQuery getOrganizationReportQuery,
|
||||
IAddOrganizationReportCommand addOrganizationReportCommand,
|
||||
IUpdateOrganizationReportCommand updateOrganizationReportCommand,
|
||||
IUpdateOrganizationReportSummaryCommand updateOrganizationReportSummaryCommand,
|
||||
IGetOrganizationReportSummaryDataQuery getOrganizationReportSummaryDataQuery,
|
||||
IGetOrganizationReportSummaryDataByDateRangeQuery getOrganizationReportSummaryDataByDateRangeQuery,
|
||||
IGetOrganizationReportDataQuery getOrganizationReportDataQuery,
|
||||
IUpdateOrganizationReportDataCommand updateOrganizationReportDataCommand,
|
||||
IGetOrganizationReportApplicationDataQuery getOrganizationReportApplicationDataQuery,
|
||||
IUpdateOrganizationReportApplicationDataCommand updateOrganizationReportApplicationDataCommand
|
||||
)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_getOrganizationReportQuery = getOrganizationReportQuery;
|
||||
_addOrganizationReportCommand = addOrganizationReportCommand;
|
||||
_updateOrganizationReportCommand = updateOrganizationReportCommand;
|
||||
_updateOrganizationReportSummaryCommand = updateOrganizationReportSummaryCommand;
|
||||
_getOrganizationReportSummaryDataQuery = getOrganizationReportSummaryDataQuery;
|
||||
_getOrganizationReportSummaryDataByDateRangeQuery = getOrganizationReportSummaryDataByDateRangeQuery;
|
||||
_getOrganizationReportDataQuery = getOrganizationReportDataQuery;
|
||||
_updateOrganizationReportDataCommand = updateOrganizationReportDataCommand;
|
||||
_getOrganizationReportApplicationDataQuery = getOrganizationReportApplicationDataQuery;
|
||||
_updateOrganizationReportApplicationDataCommand = updateOrganizationReportApplicationDataCommand;
|
||||
}
|
||||
|
||||
#region Whole OrganizationReport Endpoints
|
||||
|
||||
[HttpGet("{organizationId}/latest")]
|
||||
public async Task<IActionResult> GetLatestOrganizationReportAsync(Guid organizationId)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var latestReport = await _getOrganizationReportQuery.GetLatestOrganizationReportAsync(organizationId);
|
||||
|
||||
return Ok(latestReport);
|
||||
}
|
||||
|
||||
[HttpGet("{organizationId}/{reportId}")]
|
||||
public async Task<IActionResult> GetOrganizationReportAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId);
|
||||
|
||||
if (report == null)
|
||||
{
|
||||
throw new NotFoundException("Report not found for the specified organization.");
|
||||
}
|
||||
|
||||
if (report.OrganizationId != organizationId)
|
||||
{
|
||||
throw new BadRequestException("Invalid report ID");
|
||||
}
|
||||
|
||||
return Ok(report);
|
||||
}
|
||||
|
||||
[HttpPost("{organizationId}")]
|
||||
public async Task<IActionResult> CreateOrganizationReportAsync(Guid organizationId, [FromBody] AddOrganizationReportRequest request)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (request.OrganizationId != organizationId)
|
||||
{
|
||||
throw new BadRequestException("Organization ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
var report = await _addOrganizationReportCommand.AddOrganizationReportAsync(request);
|
||||
return Ok(report);
|
||||
}
|
||||
|
||||
[HttpPatch("{organizationId}/{reportId}")]
|
||||
public async Task<IActionResult> UpdateOrganizationReportAsync(Guid organizationId, [FromBody] UpdateOrganizationReportRequest request)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (request.OrganizationId != organizationId)
|
||||
{
|
||||
throw new BadRequestException("Organization ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
var updatedReport = await _updateOrganizationReportCommand.UpdateOrganizationReportAsync(request);
|
||||
return Ok(updatedReport);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
# region SummaryData Field Endpoints
|
||||
|
||||
[HttpGet("{organizationId}/data/summary")]
|
||||
public async Task<IActionResult> GetOrganizationReportSummaryDataByDateRangeAsync(
|
||||
Guid organizationId, [FromQuery] DateTime startDate, [FromQuery] DateTime endDate)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (organizationId.Equals(null))
|
||||
{
|
||||
throw new BadRequestException("Organization ID is required.");
|
||||
}
|
||||
|
||||
var summaryDataList = await _getOrganizationReportSummaryDataByDateRangeQuery
|
||||
.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
|
||||
|
||||
return Ok(summaryDataList);
|
||||
}
|
||||
|
||||
[HttpGet("{organizationId}/data/summary/{reportId}")]
|
||||
public async Task<IActionResult> GetOrganizationReportSummaryAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var summaryData =
|
||||
await _getOrganizationReportSummaryDataQuery.GetOrganizationReportSummaryDataAsync(organizationId, reportId);
|
||||
|
||||
if (summaryData == null)
|
||||
{
|
||||
throw new NotFoundException("Report not found for the specified organization.");
|
||||
}
|
||||
|
||||
return Ok(summaryData);
|
||||
}
|
||||
|
||||
[HttpPatch("{organizationId}/data/summary/{reportId}")]
|
||||
public async Task<IActionResult> UpdateOrganizationReportSummaryAsync(Guid organizationId, Guid reportId, [FromBody] UpdateOrganizationReportSummaryRequest request)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (request.OrganizationId != organizationId)
|
||||
{
|
||||
throw new BadRequestException("Organization ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
if (request.ReportId != reportId)
|
||||
{
|
||||
throw new BadRequestException("Report ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
var updatedReport = await _updateOrganizationReportSummaryCommand.UpdateOrganizationReportSummaryAsync(request);
|
||||
|
||||
return Ok(updatedReport);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReportData Field Endpoints
|
||||
|
||||
[HttpGet("{organizationId}/data/report/{reportId}")]
|
||||
public async Task<IActionResult> GetOrganizationReportDataAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var reportData = await _getOrganizationReportDataQuery.GetOrganizationReportDataAsync(organizationId, reportId);
|
||||
|
||||
if (reportData == null)
|
||||
{
|
||||
throw new NotFoundException("Organization report data not found.");
|
||||
}
|
||||
|
||||
return Ok(reportData);
|
||||
}
|
||||
|
||||
[HttpPatch("{organizationId}/data/report/{reportId}")]
|
||||
public async Task<IActionResult> UpdateOrganizationReportDataAsync(Guid organizationId, Guid reportId, [FromBody] UpdateOrganizationReportDataRequest request)
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (request.OrganizationId != organizationId)
|
||||
{
|
||||
throw new BadRequestException("Organization ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
if (request.ReportId != reportId)
|
||||
{
|
||||
throw new BadRequestException("Report ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
var updatedReport = await _updateOrganizationReportDataCommand.UpdateOrganizationReportDataAsync(request);
|
||||
return Ok(updatedReport);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ApplicationData Field Endpoints
|
||||
|
||||
[HttpGet("{organizationId}/data/application/{reportId}")]
|
||||
public async Task<IActionResult> GetOrganizationReportApplicationDataAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var applicationData = await _getOrganizationReportApplicationDataQuery.GetOrganizationReportApplicationDataAsync(organizationId, reportId);
|
||||
|
||||
if (applicationData == null)
|
||||
{
|
||||
throw new NotFoundException("Organization report application data not found.");
|
||||
}
|
||||
|
||||
return Ok(applicationData);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPatch("{organizationId}/data/application/{reportId}")]
|
||||
public async Task<IActionResult> UpdateOrganizationReportApplicationDataAsync(Guid organizationId, Guid reportId, [FromBody] UpdateOrganizationReportApplicationDataRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (!await _currentContext.AccessReports(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (request.OrganizationId != organizationId)
|
||||
{
|
||||
throw new BadRequestException("Organization ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
if (request.Id != reportId)
|
||||
{
|
||||
throw new BadRequestException("Report ID in the request body must match the route parameter");
|
||||
}
|
||||
|
||||
var updatedReport = await _updateOrganizationReportApplicationDataCommand.UpdateOrganizationReportApplicationDataAsync(request);
|
||||
|
||||
|
||||
|
||||
return Ok(updatedReport);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -25,7 +25,6 @@ public class ReportsController : Controller
|
||||
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
|
||||
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
|
||||
private readonly IAddOrganizationReportCommand _addOrganizationReportCommand;
|
||||
private readonly IDropOrganizationReportCommand _dropOrganizationReportCommand;
|
||||
private readonly IGetOrganizationReportQuery _getOrganizationReportQuery;
|
||||
private readonly ILogger<ReportsController> _logger;
|
||||
|
||||
@@ -38,7 +37,6 @@ public class ReportsController : Controller
|
||||
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand,
|
||||
IGetOrganizationReportQuery getOrganizationReportQuery,
|
||||
IAddOrganizationReportCommand addOrganizationReportCommand,
|
||||
IDropOrganizationReportCommand dropOrganizationReportCommand,
|
||||
ILogger<ReportsController> logger
|
||||
)
|
||||
{
|
||||
@@ -50,7 +48,6 @@ public class ReportsController : Controller
|
||||
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
|
||||
_getOrganizationReportQuery = getOrganizationReportQuery;
|
||||
_addOrganizationReportCommand = addOrganizationReportCommand;
|
||||
_dropOrganizationReportCommand = dropOrganizationReportCommand;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -209,195 +206,4 @@ public class ReportsController : Controller
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Organization Report Summary for an organization.
|
||||
/// This includes the latest report's encrypted data, encryption key, and date.
|
||||
/// This is a mock implementation and should be replaced with actual data retrieval logic.
|
||||
/// </summary>
|
||||
/// <param name="orgId"></param>
|
||||
/// <param name="from">Min date (example: 2023-01-01)</param>
|
||||
/// <param name="to">Max date (example: 2023-12-31)</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotFoundException"></exception>
|
||||
[HttpGet("organization-report-summary/{orgId}")]
|
||||
public IEnumerable<OrganizationReportSummaryModel> GetOrganizationReportSummary(
|
||||
[FromRoute] Guid orgId,
|
||||
[FromQuery] DateOnly from,
|
||||
[FromQuery] DateOnly to)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
GuardOrganizationAccess(orgId);
|
||||
|
||||
// FIXME: remove this mock class when actual data retrieval is implemented
|
||||
return MockOrganizationReportSummary.GetMockData()
|
||||
.Where(_ => _.OrganizationId == orgId
|
||||
&& _.Date >= from.ToDateTime(TimeOnly.MinValue)
|
||||
&& _.Date <= to.ToDateTime(TimeOnly.MaxValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Organization Report Summary for an organization.
|
||||
/// This is a mock implementation and should be replaced with actual creation logic.
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns>Returns 204 Created with the created OrganizationReportSummaryModel</returns>
|
||||
/// <exception cref="NotFoundException"></exception>
|
||||
[HttpPost("organization-report-summary")]
|
||||
public IActionResult CreateOrganizationReportSummary([FromBody] OrganizationReportSummaryModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
GuardOrganizationAccess(model.OrganizationId);
|
||||
|
||||
// TODO: Implement actual creation logic
|
||||
|
||||
// Returns 204 No Content as a placeholder
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPut("organization-report-summary")]
|
||||
public IActionResult UpdateOrganizationReportSummary([FromBody] OrganizationReportSummaryModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
GuardOrganizationAccess(model.OrganizationId);
|
||||
|
||||
// TODO: Implement actual update logic
|
||||
|
||||
// Returns 204 No Content as a placeholder
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private void GuardOrganizationAccess(Guid organizationId)
|
||||
{
|
||||
if (!_currentContext.AccessReports(organizationId).Result)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: remove this mock class when actual data retrieval is implemented
|
||||
private class MockOrganizationReportSummary
|
||||
{
|
||||
public static List<OrganizationReportSummaryModel> GetMockData()
|
||||
{
|
||||
return new List<OrganizationReportSummaryModel>
|
||||
{
|
||||
new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||
EncryptedData = "2.EtCcxDEBoF1MYChYHC4Q1w==|RyZ07R7qEFBbc/ICLFpEMockL9K+PD6rOod6DGHHrkaRLHUDqDwmxbu3jnD0cg8s7GIYmp0jApHXC+82QdApk87pA0Kr8fN2Rj0+8bDQCjhKfoRTipAB25S/n2E+ttjvlFfag92S66XqUH9S/eZw/Q==|0bPfykHk3SqS/biLNcNoYtH6YTstBEKu3AhvdZZLxhU=",
|
||||
EncryptionKey = "2.Dd/TtdNwxWdYg9+fRkxh6w==|8KAiK9SoadgFRmyVOchd4tNh2vErD1Rv9x1gqtsE5tzxKE/V/5kkr1WuVG+QpEj//YaQt221UEMESRSXicZ7a9cB6xXLBkbbFwmecQRJVBs=|902em44n9cwciZzYrYuX6MRzRa+4hh1HHfNAxyJx/IM=",
|
||||
Date = DateTime.UtcNow
|
||||
},
|
||||
new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||
EncryptedData = "2.HvY4fAvbzYV1hqa3255m5Q==|WcKga2Wka5i8fVso8MgjzfBAwxaqdhZDL3bnvhDsisZ0r9lNKQcG3YUQSFpJxr74cgg5QRQaFieCUe2YppciHDT6bsaE2VzFce3cNNB821uTFqnlJClkGJpG1nGvPupdErrg4Ik57WenEzYesmR4pw==|F0aJfF+1MlPm+eAlQnDgFnwfv198N9VtPqFJa4+UFqk=",
|
||||
EncryptionKey = "2.ctMgLN4ycPusbQArG/uiag==|NtqiQsAoUxMSTBQsxAMyVLWdt5lVEUGZQNxZSBU4l76ywH2f6dx5FWFrcF3t3GBqy5yDoc5eBg0VlJDW9coqzp8j9n8h1iMrtmXPyBMAhbc=|pbH+w68BUdUKYCfNRpjd8NENw2lZ0vfxgMuTrsrRCTQ=",
|
||||
Date = DateTime.UtcNow.AddMonths(-1)
|
||||
},
|
||||
new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||
EncryptedData = "2.NH4qLZYUkz/+qpB/mRsLTA==|LEFt05jJz0ngh+Hl5lqk6kebj7lZMefA3eFdL1kLJSGdD3uTOngRwH7GXLQNFeQOxutnLX9YUILbUEPwaM8gCwNQ1KWYdB1Z+Ky4nzKRb60N7L5aTA2za6zXTIdjv7Zwhg0jPZ6sPevTuvSyqjMCuA==|Uuu6gZaF0wvB2mHFwtvHegMxfe8DgsYWTRfGiVn4lkM=",
|
||||
EncryptionKey = "2.3YwG78ykSxAn44NcymdG4w==|4jfn0nLoFielicAFbmq27DNUUjV4SwGePnjYRmOa7hk4pEPnQRS3MsTJFbutVyXOgKFY9Yn2yGFZownY9EmXOMM+gHPD0t6TfzUKqQcRyuI=|wasP9zZEL9mFH5HzJYrMxnKUr/XlFKXCxG9uW66uaPU=",
|
||||
Date = DateTime.UtcNow.AddMonths(-1)
|
||||
},
|
||||
new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||
EncryptedData = "2.YmKWj/707wDPONh+JXPBOw==|Fx4jcUHmnUnSMCU8vdThMSYpDyKPnC09TxpSbNxia0M6MFbd5WHElcVribrYgTENyU0HlqPW43hThJ6xXCM0EjEWP7/jb/0l07vMNkA7sDYq+czf0XnYZgZSGKh06wFVz8xkhaPTdsiO4CXuMsoH+w==|DDVwVFHzdfbPQe3ycCx82eYVHDW97V/eWTPsNpHX/+U=",
|
||||
EncryptionKey = "2.f/U45I7KF+JKfnvOArUyaw==|zNhhS2q2WwBl6SqLWMkxrXC8EX91Ra9LJExywkJhsRbxubRLt7fK+YWc8T1LUaDmMwJ3G8buSPGzyacKX0lnUR33dW6DIaLNgRZ/ekb/zkg=|qFoIZWwS0foiiIOyikFRwQKmmmI2HeyHcOVklJnIILI=",
|
||||
Date = DateTime.UtcNow.AddMonths(-1)
|
||||
},
|
||||
new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||
EncryptedData = "2.WYauwooJUEY3kZsDPphmrA==|oguYW6h10A4GxK4KkRS0X32qSTekU2CkGqNDNGfisUgvJzsyoVTafO9sVcdPdg4BUM7YNkPMjYiKEc5jMHkIgLzbnM27jcGvMJrrccSrLHiWL6/mEiqQkV3TlfiZF9i3wqj1ITsYRzM454uNle6Wrg==|uR67aFYb1i5LSidWib0iTf8091l8GY5olHkVXse3CAw=",
|
||||
EncryptionKey = "2.ZyV9+9A2cxNaf8dfzfbnlA==|hhorBpVkcrrhTtNmd6SNHYI8gPNokGLOC22Vx8Qa/AotDAcyuYWw56zsawMnzpAdJGEJFtszKM2+VUVOcroCTMWHpy8yNf/kZA6uPk3Lz3s=|ASzVeJf+K1ZB8NXuypamRBGRuRq0GUHZBEy5r/O7ORY=",
|
||||
Date = DateTime.UtcNow.AddMonths(-1)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,15 @@ public class OrganizationReport : ITableObject<Guid>
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public string ReportData { get; set; } = string.Empty;
|
||||
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public string ContentEncryptionKey { get; set; } = string.Empty;
|
||||
|
||||
public string? SummaryData { get; set; } = null;
|
||||
public string? ApplicationData { get; set; } = null;
|
||||
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
Id = CoreHelpers.GenerateComb();
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Bit.Core.Dirt.Models.Data;
|
||||
|
||||
public class OrganizationReportApplicationDataResponse
|
||||
{
|
||||
public string? ApplicationData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Bit.Core.Dirt.Models.Data;
|
||||
|
||||
public class OrganizationReportDataResponse
|
||||
{
|
||||
public string? ReportData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Bit.Core.Dirt.Models.Data;
|
||||
|
||||
public class OrganizationReportSummaryDataResponse
|
||||
{
|
||||
public string? SummaryData { get; set; }
|
||||
}
|
||||
@@ -26,12 +26,12 @@ public class AddOrganizationReportCommand : IAddOrganizationReportCommand
|
||||
|
||||
public async Task<OrganizationReport> AddOrganizationReportAsync(AddOrganizationReportRequest request)
|
||||
{
|
||||
_logger.LogInformation("Adding organization report for organization {organizationId}", request.OrganizationId);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Adding organization report for organization {organizationId}", request.OrganizationId);
|
||||
|
||||
var (isValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogInformation("Failed to add organization {organizationId} report: {errorMessage}", request.OrganizationId, errorMessage);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Failed to add organization {organizationId} report: {errorMessage}", request.OrganizationId, errorMessage);
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
@@ -39,15 +39,18 @@ public class AddOrganizationReportCommand : IAddOrganizationReportCommand
|
||||
{
|
||||
OrganizationId = request.OrganizationId,
|
||||
ReportData = request.ReportData,
|
||||
Date = request.Date == default ? DateTime.UtcNow : request.Date,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
ContentEncryptionKey = request.ContentEncryptionKey,
|
||||
SummaryData = request.SummaryData,
|
||||
ApplicationData = request.ApplicationData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
organizationReport.SetNewId();
|
||||
|
||||
var data = await _organizationReportRepo.CreateAsync(organizationReport);
|
||||
|
||||
_logger.LogInformation("Successfully added organization report for organization {organizationId}, {organizationReportId}",
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully added organization report for organization {organizationId}, {organizationReportId}",
|
||||
request.OrganizationId, data.Id);
|
||||
|
||||
return data;
|
||||
@@ -63,12 +66,26 @@ public class AddOrganizationReportCommand : IAddOrganizationReportCommand
|
||||
return (false, "Invalid Organization");
|
||||
}
|
||||
|
||||
// ensure that we have report data
|
||||
if (string.IsNullOrWhiteSpace(request.ContentEncryptionKey))
|
||||
{
|
||||
return (false, "Content Encryption Key is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ReportData))
|
||||
{
|
||||
return (false, "Report Data is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.SummaryData))
|
||||
{
|
||||
return (false, "Summary Data is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ApplicationData))
|
||||
{
|
||||
return (false, "Application Data is required");
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class DropOrganizationReportCommand : IDropOrganizationReportCommand
|
||||
{
|
||||
private IOrganizationReportRepository _organizationReportRepo;
|
||||
private ILogger<DropOrganizationReportCommand> _logger;
|
||||
|
||||
public DropOrganizationReportCommand(
|
||||
IOrganizationReportRepository organizationReportRepository,
|
||||
ILogger<DropOrganizationReportCommand> logger)
|
||||
{
|
||||
_organizationReportRepo = organizationReportRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task DropOrganizationReportAsync(DropOrganizationReportRequest request)
|
||||
{
|
||||
_logger.LogInformation("Dropping organization report for organization {organizationId}",
|
||||
request.OrganizationId);
|
||||
|
||||
var data = await _organizationReportRepo.GetByOrganizationIdAsync(request.OrganizationId);
|
||||
if (data == null || data.Count() == 0)
|
||||
{
|
||||
_logger.LogInformation("No organization reports found for organization {organizationId}", request.OrganizationId);
|
||||
throw new BadRequestException("No data found.");
|
||||
}
|
||||
|
||||
data
|
||||
.Where(_ => request.OrganizationReportIds.Contains(_.Id))
|
||||
.ToList()
|
||||
.ForEach(async reportId =>
|
||||
{
|
||||
_logger.LogInformation("Dropping organization report {organizationReportId} for organization {organizationId}",
|
||||
reportId, request.OrganizationId);
|
||||
|
||||
await _organizationReportRepo.DeleteAsync(reportId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class GetOrganizationReportApplicationDataQuery : IGetOrganizationReportApplicationDataQuery
|
||||
{
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<GetOrganizationReportApplicationDataQuery> _logger;
|
||||
|
||||
public GetOrganizationReportApplicationDataQuery(
|
||||
IOrganizationReportRepository organizationReportRepo,
|
||||
ILogger<GetOrganizationReportApplicationDataQuery> logger)
|
||||
{
|
||||
_organizationReportRepo = organizationReportRepo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportApplicationDataResponse> GetOrganizationReportApplicationDataAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Fetching organization report application data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
|
||||
if (organizationId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportApplicationDataAsync called with empty OrganizationId");
|
||||
throw new BadRequestException("OrganizationId is required.");
|
||||
}
|
||||
|
||||
if (reportId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportApplicationDataAsync called with empty ReportId");
|
||||
throw new BadRequestException("ReportId is required.");
|
||||
}
|
||||
|
||||
var applicationDataResponse = await _organizationReportRepo.GetApplicationDataAsync(reportId);
|
||||
|
||||
if (applicationDataResponse == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "No application data found for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
throw new NotFoundException("Organization report application data not found.");
|
||||
}
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully retrieved organization report application data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
|
||||
return applicationDataResponse;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error fetching organization report application data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class GetOrganizationReportDataQuery : IGetOrganizationReportDataQuery
|
||||
{
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<GetOrganizationReportDataQuery> _logger;
|
||||
|
||||
public GetOrganizationReportDataQuery(
|
||||
IOrganizationReportRepository organizationReportRepo,
|
||||
ILogger<GetOrganizationReportDataQuery> logger)
|
||||
{
|
||||
_organizationReportRepo = organizationReportRepo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportDataResponse> GetOrganizationReportDataAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Fetching organization report data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
|
||||
if (organizationId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportDataAsync called with empty OrganizationId");
|
||||
throw new BadRequestException("OrganizationId is required.");
|
||||
}
|
||||
|
||||
if (reportId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportDataAsync called with empty ReportId");
|
||||
throw new BadRequestException("ReportId is required.");
|
||||
}
|
||||
|
||||
var reportDataResponse = await _organizationReportRepo.GetReportDataAsync(reportId);
|
||||
|
||||
if (reportDataResponse == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "No report data found for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
throw new NotFoundException("Organization report data not found.");
|
||||
}
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully retrieved organization report data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
|
||||
return reportDataResponse;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error fetching organization report data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,23 @@ public class GetOrganizationReportQuery : IGetOrganizationReportQuery
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrganizationReport>> GetOrganizationReportAsync(Guid organizationId)
|
||||
public async Task<OrganizationReport> GetOrganizationReportAsync(Guid reportId)
|
||||
{
|
||||
if (organizationId == Guid.Empty)
|
||||
if (reportId == Guid.Empty)
|
||||
{
|
||||
throw new BadRequestException("OrganizationId is required.");
|
||||
throw new BadRequestException("Id of report is required.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Fetching organization reports for organization {organizationId}", organizationId);
|
||||
return await _organizationReportRepo.GetByOrganizationIdAsync(organizationId);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Fetching organization reports for organization by Id: {reportId}", reportId);
|
||||
|
||||
var results = await _organizationReportRepo.GetByIdAsync(reportId);
|
||||
|
||||
if (results == null)
|
||||
{
|
||||
throw new NotFoundException($"No report found for Id: {reportId}");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> GetLatestOrganizationReportAsync(Guid organizationId)
|
||||
@@ -37,7 +45,7 @@ public class GetOrganizationReportQuery : IGetOrganizationReportQuery
|
||||
throw new BadRequestException("OrganizationId is required.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Fetching latest organization report for organization {organizationId}", organizationId);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Fetching latest organization report for organization {organizationId}", organizationId);
|
||||
return await _organizationReportRepo.GetLatestByOrganizationIdAsync(organizationId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class GetOrganizationReportSummaryDataByDateRangeQuery : IGetOrganizationReportSummaryDataByDateRangeQuery
|
||||
{
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<GetOrganizationReportSummaryDataByDateRangeQuery> _logger;
|
||||
|
||||
public GetOrganizationReportSummaryDataByDateRangeQuery(
|
||||
IOrganizationReportRepository organizationReportRepo,
|
||||
ILogger<GetOrganizationReportSummaryDataByDateRangeQuery> logger)
|
||||
{
|
||||
_organizationReportRepo = organizationReportRepo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetOrganizationReportSummaryDataByDateRangeAsync(Guid organizationId, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Fetching organization report summary data by date range for organization {organizationId}, from {startDate} to {endDate}",
|
||||
organizationId, startDate, endDate);
|
||||
|
||||
var (isValid, errorMessage) = ValidateRequest(organizationId, startDate, endDate);
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportSummaryDataByDateRangeAsync validation failed: {errorMessage}", errorMessage);
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
IEnumerable<OrganizationReportSummaryDataResponse> summaryDataList = (await _organizationReportRepo
|
||||
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)) ??
|
||||
Enumerable.Empty<OrganizationReportSummaryDataResponse>();
|
||||
|
||||
var resultList = summaryDataList.ToList();
|
||||
|
||||
if (!resultList.Any())
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "No summary data found for organization {organizationId} in date range {startDate} to {endDate}",
|
||||
organizationId, startDate, endDate);
|
||||
return Enumerable.Empty<OrganizationReportSummaryDataResponse>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully retrieved {count} organization report summary data records for organization {organizationId} in date range {startDate} to {endDate}",
|
||||
resultList.Count, organizationId, startDate, endDate);
|
||||
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException))
|
||||
{
|
||||
_logger.LogError(ex, "Error fetching organization report summary data by date range for organization {organizationId}, from {startDate} to {endDate}",
|
||||
organizationId, startDate, endDate);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static (bool IsValid, string errorMessage) ValidateRequest(Guid organizationId, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
if (organizationId == Guid.Empty)
|
||||
{
|
||||
return (false, "OrganizationId is required");
|
||||
}
|
||||
|
||||
if (startDate == default)
|
||||
{
|
||||
return (false, "StartDate is required");
|
||||
}
|
||||
|
||||
if (endDate == default)
|
||||
{
|
||||
return (false, "EndDate is required");
|
||||
}
|
||||
|
||||
if (startDate > endDate)
|
||||
{
|
||||
return (false, "StartDate must be earlier than or equal to EndDate");
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class GetOrganizationReportSummaryDataQuery : IGetOrganizationReportSummaryDataQuery
|
||||
{
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<GetOrganizationReportSummaryDataQuery> _logger;
|
||||
|
||||
public GetOrganizationReportSummaryDataQuery(
|
||||
IOrganizationReportRepository organizationReportRepo,
|
||||
ILogger<GetOrganizationReportSummaryDataQuery> logger)
|
||||
{
|
||||
_organizationReportRepo = organizationReportRepo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportSummaryDataResponse> GetOrganizationReportSummaryDataAsync(Guid organizationId, Guid reportId)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Fetching organization report summary data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
|
||||
if (organizationId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportSummaryDataAsync called with empty OrganizationId");
|
||||
throw new BadRequestException("OrganizationId is required.");
|
||||
}
|
||||
|
||||
if (reportId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "GetOrganizationReportSummaryDataAsync called with empty ReportId");
|
||||
throw new BadRequestException("ReportId is required.");
|
||||
}
|
||||
|
||||
var summaryDataResponse = await _organizationReportRepo.GetSummaryDataAsync(reportId);
|
||||
|
||||
if (summaryDataResponse == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "No summary data found for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
throw new NotFoundException("Organization report summary data not found.");
|
||||
}
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully retrieved organization report summary data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
|
||||
return summaryDataResponse;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error fetching organization report summary data for organization {organizationId} and report {reportId}",
|
||||
organizationId, reportId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IDropOrganizationReportCommand
|
||||
{
|
||||
Task DropOrganizationReportAsync(DropOrganizationReportRequest request);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IGetOrganizationReportApplicationDataQuery
|
||||
{
|
||||
Task<OrganizationReportApplicationDataResponse> GetOrganizationReportApplicationDataAsync(Guid organizationId, Guid reportId);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IGetOrganizationReportDataQuery
|
||||
{
|
||||
Task<OrganizationReportDataResponse> GetOrganizationReportDataAsync(Guid organizationId, Guid reportId);
|
||||
}
|
||||
@@ -4,6 +4,6 @@ namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IGetOrganizationReportQuery
|
||||
{
|
||||
Task<IEnumerable<OrganizationReport>> GetOrganizationReportAsync(Guid organizationId);
|
||||
Task<OrganizationReport> GetOrganizationReportAsync(Guid organizationId);
|
||||
Task<OrganizationReport> GetLatestOrganizationReportAsync(Guid organizationId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IGetOrganizationReportSummaryDataByDateRangeQuery
|
||||
{
|
||||
Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetOrganizationReportSummaryDataByDateRangeAsync(
|
||||
Guid organizationId, DateTime startDate, DateTime endDate);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IGetOrganizationReportSummaryDataQuery
|
||||
{
|
||||
Task<OrganizationReportSummaryDataResponse> GetOrganizationReportSummaryDataAsync(Guid organizationId, Guid reportId);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IUpdateOrganizationReportApplicationDataCommand
|
||||
{
|
||||
Task<OrganizationReport> UpdateOrganizationReportApplicationDataAsync(UpdateOrganizationReportApplicationDataRequest request);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IUpdateOrganizationReportCommand
|
||||
{
|
||||
Task<OrganizationReport> UpdateOrganizationReportAsync(UpdateOrganizationReportRequest request);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IUpdateOrganizationReportDataCommand
|
||||
{
|
||||
Task<OrganizationReport> UpdateOrganizationReportDataAsync(UpdateOrganizationReportDataRequest request);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IUpdateOrganizationReportSummaryCommand
|
||||
{
|
||||
Task<OrganizationReport> UpdateOrganizationReportSummaryAsync(UpdateOrganizationReportSummaryRequest request);
|
||||
}
|
||||
@@ -14,7 +14,14 @@ public static class ReportingServiceCollectionExtensions
|
||||
services.AddScoped<IGetPasswordHealthReportApplicationQuery, GetPasswordHealthReportApplicationQuery>();
|
||||
services.AddScoped<IDropPasswordHealthReportApplicationCommand, DropPasswordHealthReportApplicationCommand>();
|
||||
services.AddScoped<IAddOrganizationReportCommand, AddOrganizationReportCommand>();
|
||||
services.AddScoped<IDropOrganizationReportCommand, DropOrganizationReportCommand>();
|
||||
services.AddScoped<IGetOrganizationReportQuery, GetOrganizationReportQuery>();
|
||||
services.AddScoped<IUpdateOrganizationReportCommand, UpdateOrganizationReportCommand>();
|
||||
services.AddScoped<IUpdateOrganizationReportSummaryCommand, UpdateOrganizationReportSummaryCommand>();
|
||||
services.AddScoped<IGetOrganizationReportSummaryDataQuery, GetOrganizationReportSummaryDataQuery>();
|
||||
services.AddScoped<IGetOrganizationReportSummaryDataByDateRangeQuery, GetOrganizationReportSummaryDataByDateRangeQuery>();
|
||||
services.AddScoped<IGetOrganizationReportDataQuery, GetOrganizationReportDataQuery>();
|
||||
services.AddScoped<IUpdateOrganizationReportDataCommand, UpdateOrganizationReportDataCommand>();
|
||||
services.AddScoped<IGetOrganizationReportApplicationDataQuery, GetOrganizationReportApplicationDataQuery>();
|
||||
services.AddScoped<IUpdateOrganizationReportApplicationDataCommand, UpdateOrganizationReportApplicationDataCommand>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,10 @@ public class AddOrganizationReportRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string ReportData { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public string ContentEncryptionKey { get; set; }
|
||||
|
||||
public string SummaryData { get; set; }
|
||||
|
||||
public string ApplicationData { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
public class GetOrganizationReportSummaryDataByDateRangeRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
public class UpdateOrganizationReportApplicationDataRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string ApplicationData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
public class UpdateOrganizationReportDataRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
public Guid ReportId { get; set; }
|
||||
public string ReportData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
public class UpdateOrganizationReportRequest
|
||||
{
|
||||
public Guid ReportId { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string ReportData { get; set; }
|
||||
public string ContentEncryptionKey { get; set; }
|
||||
public string SummaryData { get; set; } = null;
|
||||
public string ApplicationData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
|
||||
public class UpdateOrganizationReportSummaryRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
public Guid ReportId { get; set; }
|
||||
public string SummaryData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class UpdateOrganizationReportApplicationDataCommand : IUpdateOrganizationReportApplicationDataCommand
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepo;
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<UpdateOrganizationReportApplicationDataCommand> _logger;
|
||||
|
||||
public UpdateOrganizationReportApplicationDataCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationReportRepository organizationReportRepository,
|
||||
ILogger<UpdateOrganizationReportApplicationDataCommand> logger)
|
||||
{
|
||||
_organizationRepo = organizationRepository;
|
||||
_organizationReportRepo = organizationReportRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateOrganizationReportApplicationDataAsync(UpdateOrganizationReportApplicationDataRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Updating organization report application data {reportId} for organization {organizationId}",
|
||||
request.Id, request.OrganizationId);
|
||||
|
||||
var (isValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Failed to update organization report application data {reportId} for organization {organizationId}: {errorMessage}",
|
||||
request.Id, request.OrganizationId, errorMessage);
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
var existingReport = await _organizationReportRepo.GetByIdAsync(request.Id);
|
||||
if (existingReport == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} not found", request.Id);
|
||||
throw new NotFoundException("Organization report not found");
|
||||
}
|
||||
|
||||
if (existingReport.OrganizationId != request.OrganizationId)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} does not belong to organization {organizationId}",
|
||||
request.Id, request.OrganizationId);
|
||||
throw new BadRequestException("Organization report does not belong to the specified organization");
|
||||
}
|
||||
|
||||
var updatedReport = await _organizationReportRepo.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData);
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report application data {reportId} for organization {organizationId}",
|
||||
request.Id, request.OrganizationId);
|
||||
|
||||
return updatedReport;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error updating organization report application data {reportId} for organization {organizationId}",
|
||||
request.Id, request.OrganizationId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool isValid, string errorMessage)> ValidateRequestAsync(UpdateOrganizationReportApplicationDataRequest request)
|
||||
{
|
||||
if (request.OrganizationId == Guid.Empty)
|
||||
{
|
||||
return (false, "OrganizationId is required");
|
||||
}
|
||||
|
||||
if (request.Id == Guid.Empty)
|
||||
{
|
||||
return (false, "Id is required");
|
||||
}
|
||||
|
||||
var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId);
|
||||
if (organization == null)
|
||||
{
|
||||
return (false, "Invalid Organization");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ApplicationData))
|
||||
{
|
||||
return (false, "Application Data is required");
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class UpdateOrganizationReportCommand : IUpdateOrganizationReportCommand
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepo;
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<UpdateOrganizationReportCommand> _logger;
|
||||
|
||||
public UpdateOrganizationReportCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationReportRepository organizationReportRepository,
|
||||
ILogger<UpdateOrganizationReportCommand> logger)
|
||||
{
|
||||
_organizationRepo = organizationRepository;
|
||||
_organizationReportRepo = organizationReportRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateOrganizationReportAsync(UpdateOrganizationReportRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Updating organization report {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
|
||||
var (isValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Failed to update organization report {reportId} for organization {organizationId}: {errorMessage}",
|
||||
request.ReportId, request.OrganizationId, errorMessage);
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
var existingReport = await _organizationReportRepo.GetByIdAsync(request.ReportId);
|
||||
if (existingReport == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} not found", request.ReportId);
|
||||
throw new NotFoundException("Organization report not found");
|
||||
}
|
||||
|
||||
if (existingReport.OrganizationId != request.OrganizationId)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} does not belong to organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
throw new BadRequestException("Organization report does not belong to the specified organization");
|
||||
}
|
||||
|
||||
existingReport.ContentEncryptionKey = request.ContentEncryptionKey;
|
||||
existingReport.SummaryData = request.SummaryData;
|
||||
existingReport.ReportData = request.ReportData;
|
||||
existingReport.ApplicationData = request.ApplicationData;
|
||||
existingReport.RevisionDate = DateTime.UtcNow;
|
||||
|
||||
await _organizationReportRepo.UpsertAsync(existingReport);
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
|
||||
var response = await _organizationReportRepo.GetByIdAsync(request.ReportId);
|
||||
|
||||
if (response == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} not found after update", request.ReportId);
|
||||
throw new NotFoundException("Organization report not found after update");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error updating organization report {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool IsValid, string errorMessage)> ValidateRequestAsync(UpdateOrganizationReportRequest request)
|
||||
{
|
||||
if (request.OrganizationId == Guid.Empty)
|
||||
{
|
||||
return (false, "OrganizationId is required");
|
||||
}
|
||||
|
||||
if (request.ReportId == Guid.Empty)
|
||||
{
|
||||
return (false, "ReportId is required");
|
||||
}
|
||||
|
||||
var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId);
|
||||
if (organization == null)
|
||||
{
|
||||
return (false, "Invalid Organization");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ContentEncryptionKey))
|
||||
{
|
||||
return (false, "ContentEncryptionKey is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ReportData))
|
||||
{
|
||||
return (false, "Report Data is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.SummaryData))
|
||||
{
|
||||
return (false, "Summary Data is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ApplicationData))
|
||||
{
|
||||
return (false, "Application Data is required");
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class UpdateOrganizationReportDataCommand : IUpdateOrganizationReportDataCommand
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepo;
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<UpdateOrganizationReportDataCommand> _logger;
|
||||
|
||||
public UpdateOrganizationReportDataCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationReportRepository organizationReportRepository,
|
||||
ILogger<UpdateOrganizationReportDataCommand> logger)
|
||||
{
|
||||
_organizationRepo = organizationRepository;
|
||||
_organizationReportRepo = organizationReportRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateOrganizationReportDataAsync(UpdateOrganizationReportDataRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Updating organization report data {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
|
||||
var (isValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Failed to update organization report data {reportId} for organization {organizationId}: {errorMessage}",
|
||||
request.ReportId, request.OrganizationId, errorMessage);
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
var existingReport = await _organizationReportRepo.GetByIdAsync(request.ReportId);
|
||||
if (existingReport == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} not found", request.ReportId);
|
||||
throw new NotFoundException("Organization report not found");
|
||||
}
|
||||
|
||||
if (existingReport.OrganizationId != request.OrganizationId)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} does not belong to organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
throw new BadRequestException("Organization report does not belong to the specified organization");
|
||||
}
|
||||
|
||||
var updatedReport = await _organizationReportRepo.UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData);
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report data {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
|
||||
return updatedReport;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error updating organization report data {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool IsValid, string errorMessage)> ValidateRequestAsync(UpdateOrganizationReportDataRequest request)
|
||||
{
|
||||
if (request.OrganizationId == Guid.Empty)
|
||||
{
|
||||
return (false, "OrganizationId is required");
|
||||
}
|
||||
|
||||
if (request.ReportId == Guid.Empty)
|
||||
{
|
||||
return (false, "ReportId is required");
|
||||
}
|
||||
|
||||
var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId);
|
||||
if (organization == null)
|
||||
{
|
||||
return (false, "Invalid Organization");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.ReportData))
|
||||
{
|
||||
return (false, "Report Data is required");
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
|
||||
public class UpdateOrganizationReportSummaryCommand : IUpdateOrganizationReportSummaryCommand
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepo;
|
||||
private readonly IOrganizationReportRepository _organizationReportRepo;
|
||||
private readonly ILogger<UpdateOrganizationReportSummaryCommand> _logger;
|
||||
|
||||
public UpdateOrganizationReportSummaryCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationReportRepository organizationReportRepository,
|
||||
ILogger<UpdateOrganizationReportSummaryCommand> logger)
|
||||
{
|
||||
_organizationRepo = organizationRepository;
|
||||
_organizationReportRepo = organizationReportRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateOrganizationReportSummaryAsync(UpdateOrganizationReportSummaryRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Updating organization report summary {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
|
||||
var (isValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Failed to update organization report summary {reportId} for organization {organizationId}: {errorMessage}",
|
||||
request.ReportId, request.OrganizationId, errorMessage);
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
var existingReport = await _organizationReportRepo.GetByIdAsync(request.ReportId);
|
||||
if (existingReport == null)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} not found", request.ReportId);
|
||||
throw new NotFoundException("Organization report not found");
|
||||
}
|
||||
|
||||
if (existingReport.OrganizationId != request.OrganizationId)
|
||||
{
|
||||
_logger.LogWarning(Constants.BypassFiltersEventId, "Organization report {reportId} does not belong to organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
throw new BadRequestException("Organization report does not belong to the specified organization");
|
||||
}
|
||||
|
||||
var updatedReport = await _organizationReportRepo.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData);
|
||||
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report summary {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
|
||||
return updatedReport;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||
{
|
||||
_logger.LogError(ex, "Error updating organization report summary {reportId} for organization {organizationId}",
|
||||
request.ReportId, request.OrganizationId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool IsValid, string errorMessage)> ValidateRequestAsync(UpdateOrganizationReportSummaryRequest request)
|
||||
{
|
||||
if (request.OrganizationId == Guid.Empty)
|
||||
{
|
||||
return (false, "OrganizationId is required");
|
||||
}
|
||||
|
||||
if (request.ReportId == Guid.Empty)
|
||||
{
|
||||
return (false, "ReportId is required");
|
||||
}
|
||||
|
||||
var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId);
|
||||
if (organization == null)
|
||||
{
|
||||
return (false, "Invalid Organization");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.SummaryData))
|
||||
{
|
||||
return (false, "Summary Data is required");
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,25 @@
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
namespace Bit.Core.Dirt.Repositories;
|
||||
|
||||
public interface IOrganizationReportRepository : IRepository<OrganizationReport, Guid>
|
||||
{
|
||||
Task<ICollection<OrganizationReport>> GetByOrganizationIdAsync(Guid organizationId);
|
||||
|
||||
// Whole OrganizationReport methods
|
||||
Task<OrganizationReport> GetLatestByOrganizationIdAsync(Guid organizationId);
|
||||
|
||||
// SummaryData methods
|
||||
Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetSummaryDataByDateRangeAsync(Guid organizationId, DateTime startDate, DateTime endDate);
|
||||
Task<OrganizationReportSummaryDataResponse> GetSummaryDataAsync(Guid reportId);
|
||||
Task<OrganizationReport> UpdateSummaryDataAsync(Guid orgId, Guid reportId, string summaryData);
|
||||
|
||||
// ReportData methods
|
||||
Task<OrganizationReportDataResponse> GetReportDataAsync(Guid reportId);
|
||||
Task<OrganizationReport> UpdateReportDataAsync(Guid orgId, Guid reportId, string reportData);
|
||||
|
||||
// ApplicationData methods
|
||||
Task<OrganizationReportApplicationDataResponse> GetApplicationDataAsync(Guid reportId);
|
||||
Task<OrganizationReport> UpdateApplicationDataAsync(Guid orgId, Guid reportId, string applicationData);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Data;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Infrastructure.Dapper.Repositories;
|
||||
@@ -23,26 +24,153 @@ public class OrganizationReportRepository : Repository<OrganizationReport, Guid>
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<ICollection<OrganizationReport>> GetByOrganizationIdAsync(Guid organizationId)
|
||||
public async Task<OrganizationReport> GetLatestByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ReadOnlyConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<OrganizationReport>(
|
||||
$"[{Schema}].[OrganizationReport_ReadByOrganizationId]",
|
||||
var result = await connection.QuerySingleOrDefaultAsync<OrganizationReport>(
|
||||
$"[{Schema}].[OrganizationReport_GetLatestByOrganizationId]",
|
||||
new { OrganizationId = organizationId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> GetLatestByOrganizationIdAsync(Guid organizationId)
|
||||
public async Task<OrganizationReport> UpdateSummaryDataAsync(Guid organizationId, Guid reportId, string summaryData)
|
||||
{
|
||||
return await GetByOrganizationIdAsync(organizationId)
|
||||
.ContinueWith(task =>
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var reports = task.Result;
|
||||
return reports.OrderByDescending(r => r.CreationDate).FirstOrDefault();
|
||||
});
|
||||
var parameters = new
|
||||
{
|
||||
Id = reportId,
|
||||
OrganizationId = organizationId,
|
||||
SummaryData = summaryData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await connection.ExecuteAsync(
|
||||
$"[{Schema}].[OrganizationReport_UpdateSummaryData]",
|
||||
parameters,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
// Return the updated report
|
||||
return await connection.QuerySingleOrDefaultAsync<OrganizationReport>(
|
||||
$"[{Schema}].[OrganizationReport_ReadById]",
|
||||
new { Id = reportId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportSummaryDataResponse> GetSummaryDataAsync(Guid reportId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ReadOnlyConnectionString))
|
||||
{
|
||||
var result = await connection.QuerySingleOrDefaultAsync<OrganizationReportSummaryDataResponse>(
|
||||
$"[{Schema}].[OrganizationReport_GetSummaryDataById]",
|
||||
new { Id = reportId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetSummaryDataByDateRangeAsync(
|
||||
Guid organizationId,
|
||||
DateTime startDate, DateTime
|
||||
endDate)
|
||||
{
|
||||
using (var connection = new SqlConnection(ReadOnlyConnectionString))
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
StartDate = startDate,
|
||||
EndDate = endDate
|
||||
};
|
||||
|
||||
var results = await connection.QueryAsync<OrganizationReportSummaryDataResponse>(
|
||||
$"[{Schema}].[OrganizationReport_GetSummariesByDateRange]",
|
||||
parameters,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportDataResponse> GetReportDataAsync(Guid reportId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ReadOnlyConnectionString))
|
||||
{
|
||||
var result = await connection.QuerySingleOrDefaultAsync<OrganizationReportDataResponse>(
|
||||
$"[{Schema}].[OrganizationReport_GetReportDataById]",
|
||||
new { Id = reportId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateReportDataAsync(Guid organizationId, Guid reportId, string reportData)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
Id = reportId,
|
||||
ReportData = reportData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await connection.ExecuteAsync(
|
||||
$"[{Schema}].[OrganizationReport_UpdateReportData]",
|
||||
parameters,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
// Return the updated report
|
||||
return await connection.QuerySingleOrDefaultAsync<OrganizationReport>(
|
||||
$"[{Schema}].[OrganizationReport_ReadById]",
|
||||
new { Id = reportId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportApplicationDataResponse> GetApplicationDataAsync(Guid reportId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ReadOnlyConnectionString))
|
||||
{
|
||||
var result = await connection.QuerySingleOrDefaultAsync<OrganizationReportApplicationDataResponse>(
|
||||
$"[{Schema}].[OrganizationReport_GetApplicationDataById]",
|
||||
new { Id = reportId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateApplicationDataAsync(Guid organizationId, Guid reportId, string applicationData)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
Id = reportId,
|
||||
ApplicationData = applicationData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await connection.ExecuteAsync(
|
||||
$"[{Schema}].[OrganizationReport_UpdateApplicationData]",
|
||||
parameters,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
// Return the updated report
|
||||
return await connection.QuerySingleOrDefaultAsync<OrganizationReport>(
|
||||
$"[{Schema}].[OrganizationReport_ReadById]",
|
||||
new { Id = reportId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using AutoMapper;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using LinqToDB;
|
||||
@@ -19,18 +20,6 @@ public class OrganizationReportRepository :
|
||||
IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.OrganizationReports)
|
||||
{ }
|
||||
|
||||
public async Task<ICollection<OrganizationReport>> GetByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var results = await dbContext.OrganizationReports
|
||||
.Where(p => p.OrganizationId == organizationId)
|
||||
.ToListAsync();
|
||||
return Mapper.Map<ICollection<OrganizationReport>>(results);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> GetLatestByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
@@ -38,14 +27,161 @@ public class OrganizationReportRepository :
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var result = await dbContext.OrganizationReports
|
||||
.Where(p => p.OrganizationId == organizationId)
|
||||
.OrderByDescending(p => p.Date)
|
||||
.OrderByDescending(p => p.RevisionDate)
|
||||
.Take(1)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (result == null)
|
||||
return default;
|
||||
if (result == null) return default;
|
||||
|
||||
return Mapper.Map<OrganizationReport>(result);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateSummaryDataAsync(Guid organizationId, Guid reportId, string summaryData)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
// Update only SummaryData and RevisionDate
|
||||
await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId && p.OrganizationId == organizationId)
|
||||
.UpdateAsync(p => new Models.OrganizationReport
|
||||
{
|
||||
SummaryData = summaryData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
});
|
||||
|
||||
// Return the updated report
|
||||
var updatedReport = await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return Mapper.Map<OrganizationReport>(updatedReport);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportSummaryDataResponse> GetSummaryDataAsync(Guid reportId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var result = await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId)
|
||||
.Select(p => new OrganizationReportSummaryDataResponse
|
||||
{
|
||||
SummaryData = p.SummaryData
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetSummaryDataByDateRangeAsync(
|
||||
Guid organizationId,
|
||||
DateTime startDate,
|
||||
DateTime endDate)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var results = await dbContext.OrganizationReports
|
||||
.Where(p => p.OrganizationId == organizationId &&
|
||||
p.CreationDate >= startDate && p.CreationDate <= endDate)
|
||||
.Select(p => new OrganizationReportSummaryDataResponse
|
||||
{
|
||||
SummaryData = p.SummaryData
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportDataResponse> GetReportDataAsync(Guid reportId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var result = await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId)
|
||||
.Select(p => new OrganizationReportDataResponse
|
||||
{
|
||||
ReportData = p.ReportData
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateReportDataAsync(Guid organizationId, Guid reportId, string reportData)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
// Update only ReportData and RevisionDate
|
||||
await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId && p.OrganizationId == organizationId)
|
||||
.UpdateAsync(p => new Models.OrganizationReport
|
||||
{
|
||||
ReportData = reportData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
});
|
||||
|
||||
// Return the updated report
|
||||
var updatedReport = await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return Mapper.Map<OrganizationReport>(updatedReport);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReportApplicationDataResponse> GetApplicationDataAsync(Guid reportId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var result = await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId)
|
||||
.Select(p => new OrganizationReportApplicationDataResponse
|
||||
{
|
||||
ApplicationData = p.ApplicationData
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrganizationReport> UpdateApplicationDataAsync(Guid organizationId, Guid reportId, string applicationData)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
// Update only ApplicationData and RevisionDate
|
||||
await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId && p.OrganizationId == organizationId)
|
||||
.UpdateAsync(p => new Models.OrganizationReport
|
||||
{
|
||||
ApplicationData = applicationData,
|
||||
RevisionDate = DateTime.UtcNow
|
||||
});
|
||||
|
||||
// Return the updated report
|
||||
var updatedReport = await dbContext.OrganizationReports
|
||||
.Where(p => p.Id == reportId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return Mapper.Map<OrganizationReport>(updatedReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_Create]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Date DATETIME2(7),
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@ContentEncryptionKey VARCHAR(MAX)
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@ContentEncryptionKey VARCHAR(MAX),
|
||||
@SummaryData NVARCHAR(MAX),
|
||||
@ApplicationData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
SET NOCOUNT ON;
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
INSERT INTO [dbo].[OrganizationReport](
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[Date],
|
||||
[ReportData],
|
||||
[CreationDate],
|
||||
[ContentEncryptionKey]
|
||||
)
|
||||
VALUES (
|
||||
@Id,
|
||||
@OrganizationId,
|
||||
@Date,
|
||||
@ReportData,
|
||||
@CreationDate,
|
||||
@ContentEncryptionKey
|
||||
|
||||
INSERT INTO [dbo].[OrganizationReport](
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[ReportData],
|
||||
[CreationDate],
|
||||
[ContentEncryptionKey],
|
||||
[SummaryData],
|
||||
[ApplicationData],
|
||||
[RevisionDate]
|
||||
)
|
||||
VALUES (
|
||||
@Id,
|
||||
@OrganizationId,
|
||||
@ReportData,
|
||||
@CreationDate,
|
||||
@ContentEncryptionKey,
|
||||
@SummaryData,
|
||||
@ApplicationData,
|
||||
@RevisionDate
|
||||
);
|
||||
END
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_GetApplicationDataById]
|
||||
@Id UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[ApplicationData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [Id] = @Id
|
||||
END
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_GetLatestByOrganizationId]
|
||||
@OrganizationId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT TOP 1
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[ReportData],
|
||||
[CreationDate],
|
||||
[ContentEncryptionKey],
|
||||
[SummaryData],
|
||||
[ApplicationData],
|
||||
[RevisionDate]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [OrganizationId] = @OrganizationId
|
||||
ORDER BY [RevisionDate] DESC
|
||||
END
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_GetReportDataById]
|
||||
@Id UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[ReportData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [Id] = @Id
|
||||
END
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_GetSummariesByDateRange]
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@StartDate DATETIME2(7),
|
||||
@EndDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[SummaryData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [OrganizationId] = @OrganizationId
|
||||
AND [RevisionDate] >= @StartDate
|
||||
AND [RevisionDate] <= @EndDate
|
||||
ORDER BY [RevisionDate] DESC
|
||||
END
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_GetSummaryDataById]
|
||||
@Id UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[SummaryData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [Id] = @Id
|
||||
END
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_ReadByOrganizationId]
|
||||
@OrganizationId UNIQUEIDENTIFIER
|
||||
AS
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [OrganizationId] = @OrganizationId;
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_Update]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@ContentEncryptionKey VARCHAR(MAX),
|
||||
@SummaryData NVARCHAR(MAX),
|
||||
@ApplicationData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[ReportData] = @ReportData,
|
||||
[CreationDate] = @CreationDate,
|
||||
[ContentEncryptionKey] = @ContentEncryptionKey,
|
||||
[SummaryData] = @SummaryData,
|
||||
[ApplicationData] = @ApplicationData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id;
|
||||
END;
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_UpdateApplicationData]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ApplicationData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[ApplicationData] = @ApplicationData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id
|
||||
AND [OrganizationId] = @OrganizationId;
|
||||
END
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_UpdateReportData]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[ReportData] = @ReportData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id
|
||||
AND [OrganizationId] = @OrganizationId;
|
||||
END
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE PROCEDURE [dbo].[OrganizationReport_UpdateSummaryData]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@SummaryData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[SummaryData] = @SummaryData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id
|
||||
AND [OrganizationId] = @OrganizationId;
|
||||
END
|
||||
@@ -1,19 +1,24 @@
|
||||
CREATE TABLE [dbo].[OrganizationReport] (
|
||||
[Id] UNIQUEIDENTIFIER NOT NULL,
|
||||
[OrganizationId] UNIQUEIDENTIFIER NOT NULL,
|
||||
[Date] DATETIME2 (7) NOT NULL,
|
||||
[ReportData] NVARCHAR(MAX) NOT NULL,
|
||||
[CreationDate] DATETIME2 (7) NOT NULL,
|
||||
[ContentEncryptionKey] VARCHAR(MAX) NOT NULL,
|
||||
[SummaryData] NVARCHAR(MAX) NULL,
|
||||
[ApplicationData] NVARCHAR(MAX) NULL,
|
||||
[RevisionDate] DATETIME2 (7) NULL,
|
||||
CONSTRAINT [PK_OrganizationReport] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||
CONSTRAINT [FK_OrganizationReport_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id])
|
||||
);
|
||||
);
|
||||
GO
|
||||
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_OrganizationReport_OrganizationId]
|
||||
ON [dbo].[OrganizationReport]([OrganizationId] ASC);
|
||||
ON [dbo].[OrganizationReport] ([OrganizationId] ASC);
|
||||
GO
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_OrganizationReport_OrganizationId_Date]
|
||||
ON [dbo].[OrganizationReport]([OrganizationId] ASC, [Date] DESC);
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_OrganizationReport_OrganizationId_RevisionDate]
|
||||
ON [dbo].[OrganizationReport]([OrganizationId] ASC, [RevisionDate] DESC);
|
||||
GO
|
||||
|
||||
|
||||
1165
test/Api.Test/Dirt/OrganizationReportsControllerTests.cs
Normal file
1165
test/Api.Test/Dirt/OrganizationReportsControllerTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,12 @@
|
||||
using AutoFixture;
|
||||
using Bit.Api.Dirt.Controllers;
|
||||
using Bit.Api.Dirt.Models;
|
||||
using Bit.Api.Dirt.Models.Response;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@@ -144,323 +142,4 @@ public class ReportsControllerTests
|
||||
_.OrganizationId == request.OrganizationId &&
|
||||
_.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);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void CreateOrganizationReportSummary_ReturnsNoContent_WhenAccessGranted(SutProvider<ReportsController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key",
|
||||
Date = DateTime.UtcNow
|
||||
};
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.CreateOrganizationReportSummary(model);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NoContentResult>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void CreateOrganizationReportSummary_ThrowsNotFoundException_WhenAccessDenied(SutProvider<ReportsController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key",
|
||||
Date = DateTime.UtcNow
|
||||
};
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(false);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<Bit.Core.Exceptions.NotFoundException>(
|
||||
() => sutProvider.Sut.CreateOrganizationReportSummary(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void GetOrganizationReportSummary_ThrowsNotFoundException_WhenAccessDenied(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(false);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<Bit.Core.Exceptions.NotFoundException>(
|
||||
() => sutProvider.Sut.GetOrganizationReportSummary(orgId, DateOnly.FromDateTime(DateTime.UtcNow), DateOnly.FromDateTime(DateTime.UtcNow)));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void GetOrganizationReportSummary_returnsExpectedResult(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var dates = new[]
|
||||
{
|
||||
DateOnly.FromDateTime(DateTime.UtcNow),
|
||||
DateOnly.FromDateTime(DateTime.UtcNow.AddMonths(-1))
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.GetOrganizationReportSummary(orgId, dates[0], dates[1]);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void CreateOrganizationReportSummary_ReturnsNoContent_WhenModelIsValidAndAccessGranted(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key"
|
||||
};
|
||||
sutProvider.Sut.ModelState.Clear();
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.CreateOrganizationReportSummary(model);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NoContentResult>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void CreateOrganizationReportSummary_ThrowsBadRequestException_WhenModelStateIsInvalid(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key"
|
||||
};
|
||||
sutProvider.Sut.ModelState.AddModelError("key", "error");
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<BadRequestException>(() => sutProvider.Sut.CreateOrganizationReportSummary(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void UpdateOrganizationReportSummary_ReturnsNoContent_WhenModelIsValidAndAccessGranted(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key"
|
||||
};
|
||||
sutProvider.Sut.ModelState.Clear();
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.UpdateOrganizationReportSummary(model);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<NoContentResult>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void UpdateOrganizationReportSummary_ThrowsBadRequestException_WhenModelStateIsInvalid(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key"
|
||||
};
|
||||
sutProvider.Sut.ModelState.AddModelError("key", "error");
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<BadRequestException>(() => sutProvider.Sut.UpdateOrganizationReportSummary(model));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void UpdateOrganizationReportSummary_ThrowsNotFoundException_WhenAccessDenied(
|
||||
SutProvider<ReportsController> sutProvider
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var orgId = Guid.NewGuid();
|
||||
var model = new OrganizationReportSummaryModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
EncryptedData = "mock-data",
|
||||
EncryptionKey = "mock-key"
|
||||
};
|
||||
sutProvider.Sut.ModelState.Clear();
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(false);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<NotFoundException>(() => sutProvider.Sut.UpdateOrganizationReportSummary(model));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class DeleteOrganizationReportCommandTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withValidRequest_Success(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var OrganizationReports = fixture.CreateMany<OrganizationReport>(2).ToList();
|
||||
// only take one id from the list - we only want to drop one record
|
||||
var request = fixture.Build<DropOrganizationReportRequest>()
|
||||
.With(x => x.OrganizationReportIds,
|
||||
OrganizationReports.Select(x => x.Id).Take(1).ToList())
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(OrganizationReports);
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.DropOrganizationReportAsync(request);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1)
|
||||
.GetByOrganizationIdAsync(request.OrganizationId);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1)
|
||||
.DeleteAsync(Arg.Is<OrganizationReport>(_ =>
|
||||
request.OrganizationReportIds.Contains(_.Id)));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withValidRequest_nothingToDrop(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var OrganizationReports = fixture.CreateMany<OrganizationReport>(2).ToList();
|
||||
// we are passing invalid data
|
||||
var request = fixture.Build<DropOrganizationReportRequest>()
|
||||
.With(x => x.OrganizationReportIds, new List<Guid> { Guid.NewGuid() })
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(OrganizationReports);
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.DropOrganizationReportAsync(request);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1)
|
||||
.GetByOrganizationIdAsync(request.OrganizationId);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(0)
|
||||
.DeleteAsync(Arg.Any<OrganizationReport>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withNodata_fails(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
// we are passing invalid data
|
||||
var request = fixture.Build<DropOrganizationReportRequest>()
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(null as List<OrganizationReport>);
|
||||
|
||||
// Act
|
||||
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1)
|
||||
.GetByOrganizationIdAsync(request.OrganizationId);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(0)
|
||||
.DeleteAsync(Arg.Any<OrganizationReport>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withInvalidOrganizationId_ShouldThrowError(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<DropOrganizationReportRequest>();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(null as List<OrganizationReport>);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
Assert.Equal("No data found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withInvalidOrganizationReportId_ShouldThrowError(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<DropOrganizationReportRequest>();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(new List<OrganizationReport>());
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
Assert.Equal("No data found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withNullOrganizationId_ShouldThrowError(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<DropOrganizationReportRequest>()
|
||||
.With(x => x.OrganizationId, default(Guid))
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
Assert.Equal("No data found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withNullOrganizationReportIds_ShouldThrowError(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<DropOrganizationReportRequest>()
|
||||
.With(x => x.OrganizationReportIds, default(List<Guid>))
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
Assert.Equal("No data found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withEmptyOrganizationReportIds_ShouldThrowError(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<DropOrganizationReportRequest>()
|
||||
.With(x => x.OrganizationReportIds, new List<Guid>())
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
Assert.Equal("No data found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DropOrganizationReportAsync_withEmptyRequest_ShouldThrowError(
|
||||
SutProvider<DropOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var request = new DropOrganizationReportRequest();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
|
||||
Assert.Equal("No data found.", exception.Message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationReportApplicationDataQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportApplicationDataAsync_WithValidParams_ShouldReturnApplicationData(
|
||||
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
var reportId = fixture.Create<Guid>();
|
||||
var applicationDataResponse = fixture.Build<OrganizationReportApplicationDataResponse>()
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetApplicationDataAsync(reportId)
|
||||
.Returns(applicationDataResponse);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, reportId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).GetApplicationDataAsync(reportId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportApplicationDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var reportId = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(Guid.Empty, reportId));
|
||||
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetApplicationDataAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportApplicationDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, Guid.Empty));
|
||||
|
||||
Assert.Equal("ReportId is required.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetApplicationDataAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportApplicationDataAsync_WhenDataNotFound_ShouldThrowNotFoundException(
|
||||
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetApplicationDataAsync(reportId)
|
||||
.Returns((OrganizationReportApplicationDataResponse)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, reportId));
|
||||
|
||||
Assert.Equal("Organization report application data not found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportApplicationDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
var expectedMessage = "Database connection failed";
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetApplicationDataAsync(reportId)
|
||||
.Throws(new InvalidOperationException(expectedMessage));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, reportId));
|
||||
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationReportDataQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportDataAsync_WithValidParams_ShouldReturnReportData(
|
||||
SutProvider<GetOrganizationReportDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
var reportId = fixture.Create<Guid>();
|
||||
var reportDataResponse = fixture.Build<OrganizationReportDataResponse>()
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetReportDataAsync(reportId)
|
||||
.Returns(reportDataResponse);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, reportId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).GetReportDataAsync(reportId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var reportId = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportDataAsync(Guid.Empty, reportId));
|
||||
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetReportDataAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, Guid.Empty));
|
||||
|
||||
Assert.Equal("ReportId is required.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetReportDataAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportDataAsync_WhenDataNotFound_ShouldThrowNotFoundException(
|
||||
SutProvider<GetOrganizationReportDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetReportDataAsync(reportId)
|
||||
.Returns((OrganizationReportDataResponse)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, reportId));
|
||||
|
||||
Assert.Equal("Organization report data not found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<GetOrganizationReportDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
var expectedMessage = "Database connection failed";
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetReportDataAsync(reportId)
|
||||
.Throws(new InvalidOperationException(expectedMessage));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, reportId));
|
||||
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationReportQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportAsync_WithValidOrganizationId_ShouldReturnOrganizationReport(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(fixture.CreateMany<OrganizationReport>(2).ToList());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportAsync(organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.True(result.Count() == 2);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportAsync_WithInvalidOrganizationId_ShouldFail(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Is<Guid>(x => x == Guid.Empty))
|
||||
.Returns(new List<OrganizationReport>());
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(Guid.Empty));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetLatestOrganizationReportAsync_WithValidOrganizationId_ShouldReturnOrganizationReport(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetLatestByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(fixture.Create<OrganizationReport>());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetLatestOrganizationReportAsync_WithInvalidOrganizationId_ShouldFail(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetLatestByOrganizationIdAsync(Arg.Is<Guid>(x => x == Guid.Empty))
|
||||
.Returns(default(OrganizationReport));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(Guid.Empty));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportAsync_WithNoReports_ShouldReturnEmptyList(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(new List<OrganizationReport>());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportAsync(organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetLatestOrganizationReportAsync_WithNoReports_ShouldReturnNull(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetLatestByOrganizationIdAsync(Arg.Any<Guid>())
|
||||
.Returns(default(OrganizationReport));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportAsync_WithNullOrganizationId_ShouldThrowException(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = default(Guid);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(organizationId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
}
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetLatestOrganizationReportAsync_WithNullOrganizationId_ShouldThrowException(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = default(Guid);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
}
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportAsync_WithInvalidOrganizationId_ShouldThrowException(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = Guid.Empty;
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(organizationId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
}
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetLatestOrganizationReportAsync_WithInvalidOrganizationId_ShouldThrowException(
|
||||
SutProvider<GetOrganizationReportQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = Guid.Empty;
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationReportSummaryDataByDateRangeQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithValidParams_ShouldReturnSummaryData(
|
||||
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
var reportId = fixture.Create<Guid>();
|
||||
var startDate = DateTime.UtcNow.AddDays(-30);
|
||||
var endDate = DateTime.UtcNow;
|
||||
var summaryDataList = fixture.Build<OrganizationReportSummaryDataResponse>()
|
||||
.CreateMany(3);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)
|
||||
.Returns(summaryDataList);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(3, result.Count());
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var reportId = Guid.NewGuid();
|
||||
var startDate = DateTime.UtcNow.AddDays(-30);
|
||||
var endDate = DateTime.UtcNow;
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(Guid.Empty, startDate, endDate));
|
||||
|
||||
Assert.Equal("OrganizationId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive()
|
||||
.GetSummaryDataByDateRangeAsync(
|
||||
Arg.Any<Guid>(),
|
||||
Arg.Any<DateTime>(),
|
||||
Arg.Any<DateTime>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithStartDateAfterEndDate_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
var startDate = DateTime.UtcNow;
|
||||
var endDate = DateTime.UtcNow.AddDays(-30);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate));
|
||||
|
||||
Assert.Equal("StartDate must be earlier than or equal to EndDate", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetSummaryDataByDateRangeAsync(Arg.Any<Guid>(), Arg.Any<DateTime>(), Arg.Any<DateTime>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithEmptyResult_ShouldReturnEmptyList(
|
||||
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
var startDate = DateTime.UtcNow.AddDays(-30);
|
||||
var endDate = DateTime.UtcNow;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)
|
||||
.Returns(new List<OrganizationReportSummaryDataResponse>());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
var startDate = DateTime.UtcNow.AddDays(-30);
|
||||
var endDate = DateTime.UtcNow;
|
||||
var expectedMessage = "Database connection failed";
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)
|
||||
.Throws(new InvalidOperationException(expectedMessage));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate));
|
||||
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Dirt.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationReportSummaryDataQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataAsync_WithValidParams_ShouldReturnSummaryData(
|
||||
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var organizationId = fixture.Create<Guid>();
|
||||
var reportId = fixture.Create<Guid>();
|
||||
var summaryDataResponse = fixture.Build<OrganizationReportSummaryDataResponse>()
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetSummaryDataAsync(reportId)
|
||||
.Returns(summaryDataResponse);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, reportId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).GetSummaryDataAsync(reportId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var reportId = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(Guid.Empty, reportId));
|
||||
|
||||
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetSummaryDataAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
|
||||
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, Guid.Empty));
|
||||
|
||||
Assert.Equal("ReportId is required.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().GetSummaryDataAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataAsync_WhenDataNotFound_ShouldThrowNotFoundException(
|
||||
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetSummaryDataAsync(reportId)
|
||||
.Returns((OrganizationReportSummaryDataResponse)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, reportId));
|
||||
|
||||
Assert.Equal("Organization report summary data not found.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationReportSummaryDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var organizationId = Guid.NewGuid();
|
||||
var reportId = Guid.NewGuid();
|
||||
var expectedMessage = "Database connection failed";
|
||||
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetSummaryDataAsync(reportId)
|
||||
.Throws(new InvalidOperationException(expectedMessage));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, reportId));
|
||||
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationReportApplicationDataCommandTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithValidRequest_ShouldReturnUpdatedReport(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
|
||||
.With(x => x.Id, Guid.NewGuid())
|
||||
.With(x => x.OrganizationId, Guid.NewGuid())
|
||||
.With(x => x.ApplicationData, "updated application data")
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.Id)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
var updatedReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.Id)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.Id)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData)
|
||||
.Returns(updatedReport);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(updatedReport.Id, result.Id);
|
||||
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
|
||||
.With(x => x.OrganizationId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("OrganizationId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpdateApplicationDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithEmptyId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
|
||||
.With(x => x.Id, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Id is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpdateApplicationDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns((Organization)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Invalid Organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithEmptyApplicationData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
|
||||
.With(x => x.ApplicationData, string.Empty)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Application Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithNullApplicationData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
|
||||
.With(x => x.ApplicationData, (string)null)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Application Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithNonExistentReport_ShouldThrowNotFoundException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.Id)
|
||||
.Returns((OrganizationReport)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Organization report not found", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.Id)
|
||||
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.Id)
|
||||
.Returns(existingReport);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportApplicationDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.Id)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.Id)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData)
|
||||
.Throws(new InvalidOperationException("Database connection failed"));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
|
||||
|
||||
Assert.Equal("Database connection failed", exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationReportCommandTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithValidRequest_ShouldReturnUpdatedReport(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportRequest>()
|
||||
.With(x => x.ReportId, Guid.NewGuid())
|
||||
.With(x => x.OrganizationId, Guid.NewGuid())
|
||||
.With(x => x.ReportData, "updated report data")
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
var updatedReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.With(x => x.ReportData, request.ReportData)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpsertAsync(Arg.Any<OrganizationReport>())
|
||||
.Returns(Task.CompletedTask);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport, updatedReport);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.UpdateOrganizationReportAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(updatedReport.Id, result.Id);
|
||||
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
|
||||
Assert.Equal(updatedReport.ReportData, result.ReportData);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1).GetByIdAsync(request.OrganizationId);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(2).GetByIdAsync(request.ReportId);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).UpsertAsync(Arg.Any<OrganizationReport>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportRequest>()
|
||||
.With(x => x.OrganizationId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("OrganizationId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpsertAsync(Arg.Any<OrganizationReport>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithEmptyReportId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportRequest>()
|
||||
.With(x => x.ReportId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("ReportId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpsertAsync(Arg.Any<OrganizationReport>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportRequest>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns((Organization)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("Invalid Organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithEmptyReportData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportRequest>()
|
||||
.With(x => x.ReportData, string.Empty)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("Report Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithNullReportData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportRequest>()
|
||||
.With(x => x.ReportData, (string)null)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("Report Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithNonExistentReport_ShouldThrowNotFoundException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns((OrganizationReport)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("Organization report not found", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
|
||||
|
||||
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationReportDataCommandTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithValidRequest_ShouldReturnUpdatedReport(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
|
||||
.With(x => x.ReportId, Guid.NewGuid())
|
||||
.With(x => x.OrganizationId, Guid.NewGuid())
|
||||
.With(x => x.ReportData, "updated report data")
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
var updatedReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData)
|
||||
.Returns(updatedReport);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.UpdateOrganizationReportDataAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(updatedReport.Id, result.Id);
|
||||
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
|
||||
.With(x => x.OrganizationId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("OrganizationId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpdateReportDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
|
||||
.With(x => x.ReportId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("ReportId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpdateReportDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns((Organization)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("Invalid Organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithEmptyReportData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
|
||||
.With(x => x.ReportData, string.Empty)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("Report Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithNullReportData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
|
||||
.With(x => x.ReportData, (string)null)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("Report Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithNonExistentReport_ShouldThrowNotFoundException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns((OrganizationReport)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("Organization report not found", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData)
|
||||
.Throws(new InvalidOperationException("Database connection failed"));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
|
||||
|
||||
Assert.Equal("Database connection failed", exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Dirt.Entities;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Dirt.ReportFeatures;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationReportSummaryCommandTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithValidRequest_ShouldReturnUpdatedReport(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
|
||||
.With(x => x.ReportId, Guid.NewGuid())
|
||||
.With(x => x.OrganizationId, Guid.NewGuid())
|
||||
.With(x => x.SummaryData, "updated summary data")
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
var updatedReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData)
|
||||
.Returns(updatedReport);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(updatedReport.Id, result.Id);
|
||||
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.Received(1).UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
|
||||
.With(x => x.OrganizationId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("OrganizationId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpdateSummaryDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithEmptyReportId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
|
||||
.With(x => x.ReportId, Guid.Empty)
|
||||
.Create();
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("ReportId is required", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.DidNotReceive().UpdateSummaryDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns((Organization)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("Invalid Organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithEmptySummaryData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
|
||||
.With(x => x.SummaryData, string.Empty)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("Summary Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithNullSummaryData_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
|
||||
.With(x => x.SummaryData, (string)null)
|
||||
.Create();
|
||||
|
||||
var organization = fixture.Create<Organization>();
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("Summary Data is required", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithNonExistentReport_ShouldThrowNotFoundException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns((OrganizationReport)null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("Organization report not found", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateOrganizationReportSummaryAsync_WhenRepositoryThrowsException_ShouldPropagateException(
|
||||
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var existingReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.Id, request.ReportId)
|
||||
.With(x => x.OrganizationId, request.OrganizationId)
|
||||
.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(request.OrganizationId)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.GetByIdAsync(request.ReportId)
|
||||
.Returns(existingReport);
|
||||
sutProvider.GetDependency<IOrganizationReportRepository>()
|
||||
.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData)
|
||||
.Throws(new InvalidOperationException("Database connection failed"));
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
|
||||
|
||||
Assert.Equal("Database connection failed", exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,8 @@ public class OrganizationReportRepositoryTests
|
||||
var sqlOrganization = await sqlOrganizationRepo.CreateAsync(organization);
|
||||
|
||||
report.OrganizationId = sqlOrganization.Id;
|
||||
var sqlOrgnizationReportRecord = await sqlOrganizationReportRepo.CreateAsync(report);
|
||||
var savedSqlOrganizationReport = await sqlOrganizationReportRepo.GetByIdAsync(sqlOrgnizationReportRecord.Id);
|
||||
var sqlOrganizationReportRecord = await sqlOrganizationReportRepo.CreateAsync(report);
|
||||
var savedSqlOrganizationReport = await sqlOrganizationReportRepo.GetByIdAsync(sqlOrganizationReportRecord.Id);
|
||||
records.Add(savedSqlOrganizationReport);
|
||||
|
||||
Assert.True(records.Count == 4);
|
||||
@@ -51,17 +51,19 @@ public class OrganizationReportRepositoryTests
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task RetrieveByOrganisation_Works(
|
||||
OrganizationReportRepository sqlPasswordHealthReportApplicationRepo,
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
var (firstOrg, _) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
var (secondOrg, _) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
var (firstOrg, firstReport) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
var (secondOrg, secondReport) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
|
||||
var firstSetOfRecords = await sqlPasswordHealthReportApplicationRepo.GetByOrganizationIdAsync(firstOrg.Id);
|
||||
var nextSetOfRecords = await sqlPasswordHealthReportApplicationRepo.GetByOrganizationIdAsync(secondOrg.Id);
|
||||
var firstRetrievedReport = await sqlOrganizationReportRepo.GetByIdAsync(firstReport.Id);
|
||||
var secondRetrievedReport = await sqlOrganizationReportRepo.GetByIdAsync(secondReport.Id);
|
||||
|
||||
Assert.True(firstSetOfRecords.Count == 1 && firstSetOfRecords.First().OrganizationId == firstOrg.Id);
|
||||
Assert.True(nextSetOfRecords.Count == 1 && nextSetOfRecords.First().OrganizationId == secondOrg.Id);
|
||||
Assert.NotNull(firstRetrievedReport);
|
||||
Assert.NotNull(secondRetrievedReport);
|
||||
Assert.Equal(firstOrg.Id, firstRetrievedReport.OrganizationId);
|
||||
Assert.Equal(secondOrg.Id, secondRetrievedReport.OrganizationId);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
@@ -112,6 +114,251 @@ public class OrganizationReportRepositoryTests
|
||||
Assert.True(dbRecords.Where(_ => _ == null).Count() == 4);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetLatestByOrganizationIdAsync_ShouldReturnLatestReport(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (org, firstReport) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
|
||||
// Create a second report for the same organization with a later revision date
|
||||
var fixture = new Fixture();
|
||||
var secondReport = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, org.Id)
|
||||
.With(x => x.RevisionDate, firstReport.RevisionDate.AddMinutes(30))
|
||||
.Create();
|
||||
|
||||
await sqlOrganizationReportRepo.CreateAsync(secondReport);
|
||||
|
||||
// Act
|
||||
var latestReport = await sqlOrganizationReportRepo.GetLatestByOrganizationIdAsync(org.Id);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(latestReport);
|
||||
Assert.Equal(org.Id, latestReport.OrganizationId);
|
||||
Assert.True(latestReport.RevisionDate >= firstReport.RevisionDate);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task UpdateSummaryDataAsync_ShouldUpdateSummaryAndRevisionDate(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (_, report) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
report.RevisionDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)); // ensure old revision date
|
||||
var newSummaryData = "Updated summary data";
|
||||
var originalRevisionDate = report.RevisionDate;
|
||||
|
||||
// Act
|
||||
var updatedReport = await sqlOrganizationReportRepo.UpdateSummaryDataAsync(report.OrganizationId, report.Id, newSummaryData);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(updatedReport);
|
||||
Assert.Equal(newSummaryData, updatedReport.SummaryData);
|
||||
Assert.True(updatedReport.RevisionDate > originalRevisionDate);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetSummaryDataAsync_ShouldReturnSummaryData(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var summaryData = "Test summary data";
|
||||
var (org, report) = await CreateOrganizationAndReportWithSummaryDataAsync(
|
||||
sqlOrganizationRepo, sqlOrganizationReportRepo, summaryData);
|
||||
|
||||
// Act
|
||||
var result = await sqlOrganizationReportRepo.GetSummaryDataAsync(report.Id);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(summaryData, result.SummaryData);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetSummaryDataByDateRangeAsync_ShouldReturnFilteredResults(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var baseDate = DateTime.UtcNow;
|
||||
var startDate = baseDate.AddDays(-10);
|
||||
var endDate = baseDate.AddDays(1);
|
||||
|
||||
// Create organization first
|
||||
var fixture = new Fixture();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var org = await sqlOrganizationRepo.CreateAsync(organization);
|
||||
|
||||
// Create first report with a date within range
|
||||
var report1 = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, org.Id)
|
||||
.With(x => x.SummaryData, "Summary 1")
|
||||
.With(x => x.CreationDate, baseDate.AddDays(-5)) // Within range
|
||||
.With(x => x.RevisionDate, baseDate.AddDays(-5))
|
||||
.Create();
|
||||
await sqlOrganizationReportRepo.CreateAsync(report1);
|
||||
|
||||
// Create second report with a date within range
|
||||
var report2 = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, org.Id)
|
||||
.With(x => x.SummaryData, "Summary 2")
|
||||
.With(x => x.CreationDate, baseDate.AddDays(-3)) // Within range
|
||||
.With(x => x.RevisionDate, baseDate.AddDays(-3))
|
||||
.Create();
|
||||
await sqlOrganizationReportRepo.CreateAsync(report2);
|
||||
|
||||
// Act
|
||||
var results = await sqlOrganizationReportRepo.GetSummaryDataByDateRangeAsync(
|
||||
org.Id, startDate, endDate);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(results);
|
||||
var resultsList = results.ToList();
|
||||
Assert.True(resultsList.Count >= 2, $"Expected at least 2 results, but got {resultsList.Count}");
|
||||
Assert.All(resultsList, r => Assert.NotNull(r.SummaryData));
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetReportDataAsync_ShouldReturnReportData(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var fixture = new Fixture();
|
||||
var reportData = "Test report data";
|
||||
var (org, report) = await CreateOrganizationAndReportWithReportDataAsync(
|
||||
sqlOrganizationRepo, sqlOrganizationReportRepo, reportData);
|
||||
|
||||
// Act
|
||||
var result = await sqlOrganizationReportRepo.GetReportDataAsync(report.Id);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(reportData, result.ReportData);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task UpdateReportDataAsync_ShouldUpdateReportDataAndRevisionDate(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (org, report) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
var newReportData = "Updated report data";
|
||||
var originalRevisionDate = report.RevisionDate;
|
||||
|
||||
// Add a small delay to ensure revision date difference
|
||||
await Task.Delay(100);
|
||||
|
||||
// Act
|
||||
var updatedReport = await sqlOrganizationReportRepo.UpdateReportDataAsync(
|
||||
org.Id, report.Id, newReportData);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(updatedReport);
|
||||
Assert.Equal(org.Id, updatedReport.OrganizationId);
|
||||
Assert.Equal(report.Id, updatedReport.Id);
|
||||
Assert.Equal(newReportData, updatedReport.ReportData);
|
||||
Assert.True(updatedReport.RevisionDate >= originalRevisionDate,
|
||||
$"Expected RevisionDate {updatedReport.RevisionDate} to be >= {originalRevisionDate}");
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetApplicationDataAsync_ShouldReturnApplicationData(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var applicationData = "Test application data";
|
||||
var (org, report) = await CreateOrganizationAndReportWithApplicationDataAsync(
|
||||
sqlOrganizationRepo, sqlOrganizationReportRepo, applicationData);
|
||||
|
||||
// Act
|
||||
var result = await sqlOrganizationReportRepo.GetApplicationDataAsync(report.Id);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(applicationData, result.ApplicationData);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task UpdateApplicationDataAsync_ShouldUpdateApplicationDataAndRevisionDate(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (org, report) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
var newApplicationData = "Updated application data";
|
||||
var originalRevisionDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)); // ensure old revision date
|
||||
|
||||
// Add a small delay to ensure revision date difference
|
||||
await Task.Delay(100);
|
||||
|
||||
// Act
|
||||
var updatedReport = await sqlOrganizationReportRepo.UpdateApplicationDataAsync(
|
||||
org.Id, report.Id, newApplicationData);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(updatedReport);
|
||||
Assert.Equal(org.Id, updatedReport.OrganizationId);
|
||||
Assert.Equal(report.Id, updatedReport.Id);
|
||||
Assert.Equal(newApplicationData, updatedReport.ApplicationData);
|
||||
Assert.True(updatedReport.RevisionDate >= originalRevisionDate,
|
||||
$"Expected RevisionDate {updatedReport.RevisionDate} to be >= {originalRevisionDate}");
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetSummaryDataAsync_WithNonExistentReport_ShouldReturnNull(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (org, _) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
var nonExistentReportId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
var result = await sqlOrganizationReportRepo.GetSummaryDataAsync(nonExistentReportId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetReportDataAsync_WithNonExistentReport_ShouldReturnNull(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (org, _) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
var nonExistentReportId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
var result = await sqlOrganizationReportRepo.GetReportDataAsync(nonExistentReportId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||
public async Task GetApplicationDataAsync_WithNonExistentReport_ShouldReturnNull(
|
||||
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
// Arrange
|
||||
var (org, _) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||
var nonExistentReportId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
var result = await sqlOrganizationReportRepo.GetApplicationDataAsync(nonExistentReportId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
private async Task<(Organization, OrganizationReport)> CreateOrganizationAndReportAsync(
|
||||
IOrganizationRepository orgRepo,
|
||||
IOrganizationReportRepository orgReportRepo)
|
||||
@@ -121,6 +368,64 @@ public class OrganizationReportRepositoryTests
|
||||
|
||||
var orgReportRecord = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, organization.Id)
|
||||
.With(x => x.RevisionDate, organization.RevisionDate)
|
||||
.Create();
|
||||
|
||||
organization = await orgRepo.CreateAsync(organization);
|
||||
orgReportRecord = await orgReportRepo.CreateAsync(orgReportRecord);
|
||||
|
||||
return (organization, orgReportRecord);
|
||||
}
|
||||
|
||||
private async Task<(Organization, OrganizationReport)> CreateOrganizationAndReportWithSummaryDataAsync(
|
||||
IOrganizationRepository orgRepo,
|
||||
IOrganizationReportRepository orgReportRepo,
|
||||
string summaryData)
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
var orgReportRecord = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, organization.Id)
|
||||
.With(x => x.SummaryData, summaryData)
|
||||
.Create();
|
||||
|
||||
organization = await orgRepo.CreateAsync(organization);
|
||||
orgReportRecord = await orgReportRepo.CreateAsync(orgReportRecord);
|
||||
|
||||
return (organization, orgReportRecord);
|
||||
}
|
||||
|
||||
private async Task<(Organization, OrganizationReport)> CreateOrganizationAndReportWithReportDataAsync(
|
||||
IOrganizationRepository orgRepo,
|
||||
IOrganizationReportRepository orgReportRepo,
|
||||
string reportData)
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
var orgReportRecord = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, organization.Id)
|
||||
.With(x => x.ReportData, reportData)
|
||||
.Create();
|
||||
|
||||
organization = await orgRepo.CreateAsync(organization);
|
||||
orgReportRecord = await orgReportRepo.CreateAsync(orgReportRecord);
|
||||
|
||||
return (organization, orgReportRecord);
|
||||
}
|
||||
|
||||
private async Task<(Organization, OrganizationReport)> CreateOrganizationAndReportWithApplicationDataAsync(
|
||||
IOrganizationRepository orgRepo,
|
||||
IOrganizationReportRepository orgReportRepo,
|
||||
string applicationData)
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
var organization = fixture.Create<Organization>();
|
||||
|
||||
var orgReportRecord = fixture.Build<OrganizationReport>()
|
||||
.With(x => x.OrganizationId, organization.Id)
|
||||
.With(x => x.ApplicationData, applicationData)
|
||||
.Create();
|
||||
|
||||
organization = await orgRepo.CreateAsync(organization);
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
IF EXISTS (
|
||||
SELECT * FROM sys.indexes WHERE name = 'IX_OrganizationReport_OrganizationId_Date'
|
||||
AND object_id = OBJECT_ID('dbo.OrganizationReport')
|
||||
)
|
||||
BEGIN
|
||||
DROP INDEX [IX_OrganizationReport_OrganizationId_Date] ON [dbo].[OrganizationReport];
|
||||
END
|
||||
GO
|
||||
|
||||
IF COL_LENGTH('[dbo].[OrganizationReport]', 'Date') IS NOT NULL
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[OrganizationReport]
|
||||
DROP COLUMN [Date];
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('dbo.OrganizationReport') IS NOT NULL
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[OrganizationReport]
|
||||
ADD [SummaryData] NVARCHAR(MAX) NULL,
|
||||
[ApplicationData] NVARCHAR(MAX) NULL,
|
||||
[RevisionDate] DATETIME2 (7) NULL;
|
||||
END
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM sys.indexes WHERE name = 'IX_OrganizationReport_OrganizationId_RevisionDate'
|
||||
AND object_id = OBJECT_ID('dbo.OrganizationReport')
|
||||
)
|
||||
BEGIN
|
||||
CREATE NONCLUSTERED INDEX [IX_OrganizationReport_OrganizationId_RevisionDate]
|
||||
ON [dbo].[OrganizationReport]([OrganizationId] ASC, [RevisionDate] DESC);
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('dbo.OrganizationReportView') IS NOT NULL
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[OrganizationReportView];
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('dbo.OrganizationReportView') IS NULL
|
||||
BEGIN
|
||||
EXEC('CREATE VIEW [dbo].[OrganizationReportView]
|
||||
AS
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[OrganizationReport]');
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('dbo.OrganizationReport_Create') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[OrganizationReport_Create];
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('dbo.OrganizationReport_Create') IS NULL
|
||||
BEGIN
|
||||
EXEC('CREATE PROCEDURE [dbo].[OrganizationReport_Create]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@ContentEncryptionKey VARCHAR(MAX),
|
||||
@SummaryData NVARCHAR(MAX),
|
||||
@ApplicationData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
INSERT INTO [dbo].[OrganizationReport](
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[ReportData],
|
||||
[CreationDate],
|
||||
[ContentEncryptionKey],
|
||||
[SummaryData],
|
||||
[ApplicationData],
|
||||
[RevisionDate]
|
||||
)
|
||||
VALUES (
|
||||
@Id,
|
||||
@OrganizationId,
|
||||
@ReportData,
|
||||
@CreationDate,
|
||||
@ContentEncryptionKey,
|
||||
@SummaryData,
|
||||
@ApplicationData,
|
||||
@RevisionDate
|
||||
);
|
||||
END');
|
||||
END
|
||||
GO
|
||||
@@ -0,0 +1,156 @@
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_GetLatestByOrganizationId]
|
||||
@OrganizationId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT TOP 1
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[ReportData],
|
||||
[CreationDate],
|
||||
[ContentEncryptionKey],
|
||||
[SummaryData],
|
||||
[ApplicationData],
|
||||
[RevisionDate]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [OrganizationId] = @OrganizationId
|
||||
ORDER BY [RevisionDate] DESC
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_GetSummariesByDateRange]
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@StartDate DATETIME2(7),
|
||||
@EndDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[SummaryData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [OrganizationId] = @OrganizationId
|
||||
AND [RevisionDate] >= @StartDate
|
||||
AND [RevisionDate] <= @EndDate
|
||||
ORDER BY [RevisionDate] DESC
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_GetSummaryDataById]
|
||||
@Id UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[SummaryData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [Id] = @Id
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_UpdateSummaryData]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@SummaryData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[SummaryData] = @SummaryData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id
|
||||
AND [OrganizationId] = @OrganizationId;
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_GetReportDataById]
|
||||
@Id UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[ReportData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [Id] = @Id
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_UpdateReportData]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[ReportData] = @ReportData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id
|
||||
AND [OrganizationId] = @OrganizationId;
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_GetApplicationDataById]
|
||||
@Id UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[ApplicationData]
|
||||
FROM [dbo].[OrganizationReportView]
|
||||
WHERE [Id] = @Id;
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_UpdateApplicationData]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ApplicationData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[ApplicationData] = @ApplicationData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id
|
||||
AND [OrganizationId] = @OrganizationId;
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_Update]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@ReportData NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@ContentEncryptionKey VARCHAR(MAX),
|
||||
@SummaryData NVARCHAR(MAX),
|
||||
@ApplicationData NVARCHAR(MAX),
|
||||
@RevisionDate DATETIME2(7)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
UPDATE [dbo].[OrganizationReport]
|
||||
SET
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[ReportData] = @ReportData,
|
||||
[CreationDate] = @CreationDate,
|
||||
[ContentEncryptionKey] = @ContentEncryptionKey,
|
||||
[SummaryData] = @SummaryData,
|
||||
[ApplicationData] = @ApplicationData,
|
||||
[RevisionDate] = @RevisionDate
|
||||
WHERE [Id] = @Id;
|
||||
END
|
||||
GO
|
||||
3275
util/MySqlMigrations/Migrations/20250825064449_2025-08-22_00_AlterOrganizationReport.Designer.cs
generated
Normal file
3275
util/MySqlMigrations/Migrations/20250825064449_2025-08-22_00_AlterOrganizationReport.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class _20250822_00_AlterOrganizationReport : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Date",
|
||||
table: "OrganizationReport",
|
||||
newName: "RevisionDate");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ApplicationData",
|
||||
table: "OrganizationReport",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SummaryData",
|
||||
table: "OrganizationReport",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ApplicationData",
|
||||
table: "OrganizationReport");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SummaryData",
|
||||
table: "OrganizationReport");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "RevisionDate",
|
||||
table: "OrganizationReport",
|
||||
newName: "Date");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class _20250822_01_AddOrganizationReportStoredProcedures : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1011,6 +1011,9 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("ApplicationData")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ContentEncryptionKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@@ -1018,9 +1021,6 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
@@ -1028,6 +1028,12 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("RevisionDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("SummaryData")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
|
||||
3281
util/PostgresMigrations/Migrations/20250825064440_2025-08-22_00_AlterOrganizationReport.Designer.cs
generated
Normal file
3281
util/PostgresMigrations/Migrations/20250825064440_2025-08-22_00_AlterOrganizationReport.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class _20250822_00_AlterOrganizationReport : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Date",
|
||||
table: "OrganizationReport",
|
||||
newName: "RevisionDate");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ApplicationData",
|
||||
table: "OrganizationReport",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SummaryData",
|
||||
table: "OrganizationReport",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ApplicationData",
|
||||
table: "OrganizationReport");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SummaryData",
|
||||
table: "OrganizationReport");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "RevisionDate",
|
||||
table: "OrganizationReport",
|
||||
newName: "Date");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class _20250822_01_AddOrganizationReportStoredProcedures : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1016,6 +1016,9 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ApplicationData")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ContentEncryptionKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@@ -1023,9 +1026,6 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
@@ -1033,6 +1033,12 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("RevisionDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("SummaryData")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
|
||||
3264
util/SqliteMigrations/Migrations/20250825064445_2025-08-22_00_AlterOrganizationReport.Designer.cs
generated
Normal file
3264
util/SqliteMigrations/Migrations/20250825064445_2025-08-22_00_AlterOrganizationReport.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class _20250822_00_AlterOrganizationReport : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Date",
|
||||
table: "OrganizationReport",
|
||||
newName: "RevisionDate");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ApplicationData",
|
||||
table: "OrganizationReport",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SummaryData",
|
||||
table: "OrganizationReport",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ApplicationData",
|
||||
table: "OrganizationReport");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SummaryData",
|
||||
table: "OrganizationReport");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "RevisionDate",
|
||||
table: "OrganizationReport",
|
||||
newName: "Date");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class _20250822_01_AddOrganizationReportStoredProcedures : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1000,6 +1000,9 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ApplicationData")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ContentEncryptionKey")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1007,9 +1010,6 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1017,6 +1017,12 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("RevisionDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SummaryData")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
|
||||
Reference in New Issue
Block a user