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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 80 additions & 51 deletions DotNet/DataAcquisition.Domain/Application/Managers/QueryPlanManager.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
ο»Ώusing DataAcquisition.Domain.Application.Models;
using DataAcquisition.Domain.Application.Models.Exceptions;
using LantanaGroup.Link.DataAcquisition.Domain.Application.Models;
using LantanaGroup.Link.DataAcquisition.Domain.Application.Models.Exceptions;
using LantanaGroup.Link.DataAcquisition.Domain.Application.Validators;
using LantanaGroup.Link.DataAcquisition.Domain.Infrastructure;
using LantanaGroup.Link.DataAcquisition.Domain.Infrastructure.Entities;
using LantanaGroup.Link.DataAcquisition.Domain.Infrastructure.Interfaces;
using LantanaGroup.Link.DataAcquisition.Domain.Infrastructure.Models.QueryConfig;
using LantanaGroup.Link.Shared.Application.Models;
using Microsoft.Extensions.Logging;

Expand All @@ -23,11 +21,16 @@
{
private readonly IDatabase _database;
private readonly ILogger<QueryPlanManager> _logger;
private readonly IQueryPlanValidator _validator;

public QueryPlanManager(IDatabase database, ILogger<QueryPlanManager> logger)
public QueryPlanManager(
IDatabase database,
ILogger<QueryPlanManager> logger,
IQueryPlanValidator validator)
{
_database = database ?? throw new ArgumentNullException(nameof(database));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_validator = validator ?? throw new ArgumentNullException(nameof(validator));
}

public async Task<QueryPlanModel> AddAsync(CreateQueryPlanModel model, CancellationToken cancellationToken = default)
Expand All @@ -37,9 +40,25 @@
throw new ArgumentNullException(nameof(model), "CreateQueryPlanModel cannot be null.");
}

//// Validate query order
ValidateQueryOrder(model.InitialQueries, "InitialQueries");
ValidateQueryOrder(model.SupplementalQueries, "SupplementalQueries");
// Perform comprehensive validation
var validationResult = _validator.ValidateQueryPlan(model.InitialQueries, model.SupplementalQueries);

if (!validationResult.IsValid)
{
_logger.LogError("Query Plan validation failed for facility {FacilityId}: {Errors}",
model.FacilityId,
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
string.Join("; ", validationResult.Errors));
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

throw new BadRequestException($"Query Plan validation failed: {validationResult.GetErrorMessage()}");
}

// Log warnings if any exist
if (validationResult.Warnings.Any())
{
_logger.LogWarning("Query Plan validation warnings for facility {FacilityId}: {Warnings}",
model.FacilityId,
Comment thread
edward-miller-lcg marked this conversation as resolved.
Fixed
string.Join("; ", validationResult.Warnings));
Comment thread
edward-miller-lcg marked this conversation as resolved.
Fixed
}

var date = DateTime.UtcNow;

Expand All @@ -59,6 +78,10 @@
entity = await _database.QueryPlanRepository.AddAsync(entity);
await _database.QueryPlanRepository.SaveChangesAsync();

_logger.LogInformation("Successfully created Query Plan for facility {FacilityId} with type {Type}",
model.FacilityId,
Comment thread
edward-miller-lcg marked this conversation as resolved.
Fixed
model.Type);

return QueryPlanModel.FromDomain(entity);
}

Expand All @@ -69,82 +92,88 @@
throw new ArgumentNullException(nameof(model), "UpdateQueryPlanModel cannot be null.");
}

// Validate query order
ValidateQueryOrder(model.InitialQueries, "InitialQueries");
ValidateQueryOrder(model.SupplementalQueries, "SupplementalQueries");
// Perform comprehensive validation
var validationResult = _validator.ValidateQueryPlan(model.InitialQueries, model.SupplementalQueries);

var existingQueryPlan = await _database.QueryPlanRepository.FirstOrDefaultAsync(q => q.FacilityId == model.FacilityId && q.Type == model.Type);
if (!validationResult.IsValid)
{
_logger.LogError("Query Plan validation failed for facility {FacilityId}: {Errors}",
model.FacilityId,
Comment thread
edward-miller-lcg marked this conversation as resolved.
Fixed
string.Join("; ", validationResult.Errors));
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

throw new BadRequestException($"Query Plan validation failed: {validationResult.GetErrorMessage()}");
}

if (existingQueryPlan != null)
// Log warnings if any exist
if (validationResult.Warnings.Any())
{
existingQueryPlan.InitialQueries = model.InitialQueries;
existingQueryPlan.SupplementalQueries = model.SupplementalQueries;
existingQueryPlan.PlanName = model.PlanName;
existingQueryPlan.EHRDescription = model.EHRDescription;
existingQueryPlan.LookBack = model.LookBack;
existingQueryPlan.ModifyDate = DateTime.UtcNow;
_logger.LogWarning("Query Plan validation warnings for facility {FacilityId}: {Warnings}",
model.FacilityId,
Comment thread
edward-miller-lcg marked this conversation as resolved.
Fixed
string.Join("; ", validationResult.Warnings));
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}

await _database.QueryPlanRepository.SaveChangesAsync();
var existingQueryPlan = await _database.QueryPlanRepository.FirstOrDefaultAsync(
q => q.FacilityId == model.FacilityId && q.Type == model.Type);

return QueryPlanModel.FromDomain(existingQueryPlan);
if (existingQueryPlan == null)
{
throw new NotFoundException($"No Query Plan for FacilityId {model.FacilityId} and Type {model.Type} was found.");
}

throw new NotFoundException($"No Query Plan for FacilityId {model.FacilityId} and Type {model.Type} was found.");
existingQueryPlan.InitialQueries = model.InitialQueries;
existingQueryPlan.SupplementalQueries = model.SupplementalQueries;
existingQueryPlan.PlanName = model.PlanName;
existingQueryPlan.EHRDescription = model.EHRDescription;
existingQueryPlan.LookBack = model.LookBack;
existingQueryPlan.ModifyDate = DateTime.UtcNow;

await _database.QueryPlanRepository.SaveChangesAsync();

_logger.LogInformation("Successfully updated Query Plan for facility {FacilityId} with type {Type}",
model.FacilityId,
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
model.Type);

return QueryPlanModel.FromDomain(existingQueryPlan);
}

public async Task DeleteAsync(string facilityId, Frequency type, CancellationToken cancellationToken = default)
{
var entity = await _database.QueryPlanRepository.SingleOrDefaultAsync(q => q.FacilityId == facilityId && q.Type == type);
var entity = await _database.QueryPlanRepository.SingleOrDefaultAsync(
q => q.FacilityId == facilityId && q.Type == type);

if (entity != null)
{
_database.QueryPlanRepository.Remove(entity);
await _database.QueryPlanRepository.SaveChangesAsync();
}
else
if (entity == null)
{
throw new NotFoundException($"No Query Plan for FacilityId {facilityId} and Type {type} was found.");
}

_database.QueryPlanRepository.Remove(entity);
await _database.QueryPlanRepository.SaveChangesAsync();

_logger.LogInformation("Successfully deleted Query Plan for facility {FacilityId} with type {Type}",
facilityId,
type);
}

public async Task DeleteAllQueryPlansAsync(string facilityId, CancellationToken cancellationToken = default)
{
// Get all query plans
var allPlans = await _database.QueryPlanRepository.GetAllAsync(cancellationToken);

// Filter by facilityId
var facilityPlans = allPlans.Where(q => q.FacilityId == facilityId).ToList();

// Remove each plan individually
foreach (var plan in facilityPlans)
{
_database.QueryPlanRepository.Remove(plan);
}

// Save changes once after all removals
if (facilityPlans.Any())
{
await _database.QueryPlanRepository.SaveChangesAsync(cancellationToken);
_logger.LogInformation("Successfully deleted {Count} Query Plans for facility {FacilityId}",
facilityPlans.Count,
facilityId);
}
}

private void ValidateQueryOrder(Dictionary<string, IQueryConfig> queries, string querySetName)
{
if (queries == null) return;

bool seenReference = false;
foreach (var kvp in queries.OrderBy(q => int.TryParse(q.Key, out var i) ? i : int.MaxValue))
else
{
var config = kvp.Value;
if (config is ReferenceQueryConfig)
{
seenReference = true;
}
else if (config is ParameterQueryConfig && seenReference)
{
throw new IncorrectQueryPlanOrderException(
$"All ReferenceQueryConfig entries must appear after all ParameterQueryConfig entries in {querySetName}.");
}
_logger.LogInformation("No Query Plans found to delete for facility {FacilityId}", facilityId);
}
}
}
Loading
Loading