Skip to content

Commit

Permalink
Tenant Ad Hoc Report API
Browse files Browse the repository at this point in the history
  • Loading branch information
MontaltoNick committed Jan 13, 2025
1 parent db0fd92 commit 2ede62e
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 24 deletions.
14 changes: 14 additions & 0 deletions DotNet/Shared/Application/Models/Kafka/GenerateReportValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@


namespace LantanaGroup.Link.Shared.Application.Models.Kafka
{
public class GenerateReportValue
{
public string? ReportId { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public List<string>? ReportTypes { get; set; }
public List<string>? PatientIds { get; set; }
public bool BypassSubmission { get; set; }
}
}
3 changes: 2 additions & 1 deletion DotNet/Shared/Application/Models/KafkaTopic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ public enum KafkaTopic
SubmitReportRetry,
[StringValue("ReportScheduled-Retry")]
ReportScheduledRetry,
MeasureEvaluated
MeasureEvaluated,
GenerateReportRequested
}
129 changes: 125 additions & 4 deletions DotNet/Tenant/Controllers/FacilityController.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
using AutoMapper;
using Confluent.Kafka;
using LantanaGroup.Link.Shared.Application.Enums;
using LantanaGroup.Link.Shared.Application.Interfaces;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Models.Kafka;
using LantanaGroup.Link.Shared.Application.Models.Responses;
using LantanaGroup.Link.Tenant.Entities;
using LantanaGroup.Link.Tenant.Interfaces;
using LantanaGroup.Link.Tenant.Models;
using LantanaGroup.Link.Tenant.Services;
using Link.Authorization.Policies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Quartz;
using System.Diagnostics;
using LantanaGroup.Link.Shared.Application.Enums;
using LantanaGroup.Link.Shared.Application.Models.Responses;
using LantanaGroup.Link.Tenant.Interfaces;

namespace LantanaGroup.Link.Tenant.Controllers
{
Expand All @@ -29,8 +33,9 @@ public class FacilityController : ControllerBase

private readonly ISchedulerFactory _schedulerFactory;

private readonly IKafkaProducerFactory<string, GenerateReportValue> _adHocKafkaProderFactory;

public FacilityController(ILogger<FacilityController> logger, IFacilityConfigurationService facilityConfigurationService, ISchedulerFactory schedulerFactory)
public FacilityController(ILogger<FacilityController> logger, IFacilityConfigurationService facilityConfigurationService, ISchedulerFactory schedulerFactory, IKafkaProducerFactory<string, GenerateReportValue> adHocKafkaProderFactory)
{

_facilityConfigurationService = facilityConfigurationService;
Expand All @@ -54,6 +59,7 @@ public FacilityController(ILogger<FacilityController> logger, IFacilityConfigura

_mapperModelToDto = configModelToDto.CreateMapper();
_mapperDtoToModel = configDtoToModel.CreateMapper();
_adHocKafkaProderFactory = adHocKafkaProderFactory;
}

/// <summary>
Expand Down Expand Up @@ -271,5 +277,120 @@ public async Task<IActionResult> DeleteFacility(string facilityId, CancellationT
return NoContent();
}

[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[HttpPost]
public async Task<IActionResult> GenerateAdHocReport(string facilityId, bool? bypassSubmission, DateTime? startDate, DateTime? endDate, List<string>? reportTypes, List<string>? patientIds)
{
if (string.IsNullOrEmpty(facilityId) && await _facilityConfigurationService.GetFacilityByFacilityId(facilityId, CancellationToken.None) == null)
{
return BadRequest("Facility does not exist.");
}

if (reportTypes == null || !reportTypes.Any())
{
return BadRequest("ReportTypes must be provided.");
}

if (startDate == null || startDate == DateTime.MinValue)
{
return BadRequest("StartDate must be provided.");
}

if (endDate == null || endDate == DateTime.MinValue)
{
return BadRequest("EndDate must be provided.");
}

try
{
foreach (var rt in reportTypes)
{
//this will throw an ApplicationException if the Measure Definition does not exist.
await _facilityConfigurationService.MeasureDefinitionExists(rt);
}

var producerConfig = new ProducerConfig();

var producer = _adHocKafkaProderFactory.CreateProducer(producerConfig);

var headers = new Headers();
string correlationId = Guid.NewGuid().ToString();

headers.Add("X-Correlation-Id", System.Text.Encoding.ASCII.GetBytes(correlationId));

var message = new Message<string, GenerateReportValue>
{
Key = facilityId,
Headers = headers,
Value = new GenerateReportValue
{
ReportId = Guid.NewGuid().ToString(),
StartDate = startDate,
EndDate = endDate,
ReportTypes = reportTypes,
PatientIds = patientIds,
BypassSubmission = bypassSubmission ?? false
},
};

await producer.ProduceAsync(KafkaTopic.GenerateReportRequested.ToString(), message, CancellationToken.None);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception encountered in FacilityController.AdHocReport");
return Problem("An internal server error occurred.", statusCode: 500);
}

return Ok();
}

[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[HttpPost]
public async Task<IActionResult> RegenerateReport(string facilityId, string reportId, bool? bypassSubmission)
{
if (string.IsNullOrEmpty(facilityId) && await _facilityConfigurationService.GetFacilityByFacilityId(facilityId, CancellationToken.None) == null)
{
return BadRequest("Facility does not exist.");
}

if (string.IsNullOrEmpty(reportId))
{
return BadRequest("ReportId must be provided.");
}

try
{
var producerConfig = new ProducerConfig();

var producer = _adHocKafkaProderFactory.CreateProducer(producerConfig);

var headers = new Headers
{
{ "X-Report-Tracking-Id", System.Text.Encoding.ASCII.GetBytes(Guid.NewGuid().ToString()) }
};

var message = new Message<string, GenerateReportValue>
{
Key = facilityId,
Headers = headers,
Value = new GenerateReportValue()
{
ReportId = reportId,
BypassSubmission = bypassSubmission ?? false
},
};

await producer.ProduceAsync(KafkaTopic.GenerateReportRequested.ToString(), message, CancellationToken.None);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception encountered in FacilityController.AdHocReport");
return Problem("An internal server error occurred.", statusCode: 500);
}

return Ok();
}
}
}
3 changes: 2 additions & 1 deletion DotNet/Tenant/Interfaces/IFacilityConfigurationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ public interface IFacilityConfigurationService
Task CreateFacility(FacilityConfigModel newFacility, CancellationToken cancellationToken);
Task<List<FacilityConfigModel>> GetAllFacilities(CancellationToken cancellationToken = default);
Task<PagedConfigModel<FacilityConfigModel>> GetFacilities(string? facilityId, string? facilityName, string? sortBy, SortOrder? sortOrder, int pageSize = 10, int pageNumber = 1, CancellationToken cancellationToken = default);
Task<FacilityConfigModel> GetFacilityByFacilityId(string facilityId, CancellationToken cancellationToken);
Task<FacilityConfigModel?> GetFacilityByFacilityId(string facilityId, CancellationToken cancellationToken);
Task<FacilityConfigModel> GetFacilityById(string id, CancellationToken cancellationToken);
Task<string> RemoveFacility(string facilityId, CancellationToken cancellationToken);
Task<string> UpdateFacility(string id, FacilityConfigModel newFacility, CancellationToken cancellationToken = default);
Task MeasureDefinitionExists(String reportType);
}
}
17 changes: 9 additions & 8 deletions DotNet/Tenant/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
using Azure.Identity;
using Confluent.Kafka;
using HealthChecks.UI.Client;
using LantanaGroup.Link.Shared.Application.Extensions;
using LantanaGroup.Link.Shared.Application.Extensions.Security;
using LantanaGroup.Link.Shared.Application.Factories;
using LantanaGroup.Link.Shared.Application.Health;
using LantanaGroup.Link.Shared.Application.Interfaces;
using LantanaGroup.Link.Shared.Application.Middleware;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Models.Configs;
using LantanaGroup.Link.Shared.Application.Models.Kafka;
using LantanaGroup.Link.Shared.Application.Repositories.Interceptors;
using LantanaGroup.Link.Shared.Settings;
using LantanaGroup.Link.Tenant.Commands;
Expand All @@ -26,11 +33,6 @@
using Serilog.Settings.Configuration;
using System.Diagnostics;
using System.Reflection;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Middleware;
using LantanaGroup.Link.Shared.Application.Factories;
using Confluent.Kafka;
using LantanaGroup.Link.Shared.Application.Health;

namespace Tenant
{
Expand Down Expand Up @@ -146,9 +148,8 @@ static void RegisterServices(WebApplicationBuilder builder)
}
});



builder.Services.AddTransient<LantanaGroup.Link.Shared.Application.Interfaces.IKafkaProducerFactory<string, object>, LantanaGroup.Link.Shared.Application.Factories.KafkaProducerFactory<string, object>>();
builder.Services.AddTransient<IKafkaProducerFactory<string, GenerateReportValue>, KafkaProducerFactory<string, GenerateReportValue>>();
builder.Services.AddTransient<IKafkaProducerFactory<string, object>, KafkaProducerFactory<string, object>>();
var producer = new KafkaProducerFactory<string, object>(kafkaConnection).CreateProducer(new Confluent.Kafka.ProducerConfig());
builder.Services.AddSingleton<IProducer<string, object>>(producer);

Expand Down
16 changes: 6 additions & 10 deletions DotNet/Tenant/Services/FacilityConfigurationService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using LantanaGroup.Link.Shared.Application.Enums;
using LantanaGroup.Link.Shared.Application.Interfaces.Services.Security.Token;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Models.Configs;
using LantanaGroup.Link.Shared.Application.Models.Kafka;
using LantanaGroup.Link.Shared.Application.Models.Responses;
using LantanaGroup.Link.Shared.Application.Services.Security;
using LantanaGroup.Link.Tenant.Commands;
using LantanaGroup.Link.Tenant.Config;
using LantanaGroup.Link.Tenant.Entities;
using LantanaGroup.Link.Tenant.Interfaces;
using LantanaGroup.Link.Tenant.Models;
using LantanaGroup.Link.Tenant.Repository.Interfaces.Sql;
using LantanaGroup.Link.Tenant.Utils;
Expand All @@ -13,13 +17,7 @@
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Text;
using LantanaGroup.Link.Shared.Application.Interfaces.Services.Security.Token;
using LantanaGroup.Link.Shared.Application.Models.Responses;
using System.Linq.Expressions;
using Confluent.Kafka;
using static LantanaGroup.Link.Shared.Application.Extensions.Security.BackendAuthenticationServiceExtension;
using LantanaGroup.Link.Tenant.Interfaces;
using LantanaGroup.Link.Shared.Application.Services.Security;


namespace LantanaGroup.Link.Tenant.Services
Expand Down Expand Up @@ -100,7 +98,7 @@ public async Task<FacilityConfigModel> GetFacilityById(string id, CancellationTo
return await _facilityConfigurationRepo.GetAsync(id, cancellationToken);
}

public async Task<FacilityConfigModel> GetFacilityByFacilityId(string facilityId, CancellationToken cancellationToken)
public async Task<FacilityConfigModel?> GetFacilityByFacilityId(string facilityId, CancellationToken cancellationToken)
{
using Activity? activity = ServiceActivitySource.Instance.StartActivity("Get Facility By Facility Id Query");

Expand Down Expand Up @@ -335,11 +333,9 @@ private async Task ValidateSchedules(FacilityConfigModel facility)
{
await MeasureDefinitionExists(reportType);
}

return;
}

private async Task MeasureDefinitionExists(String reportType)
public async Task MeasureDefinitionExists(String reportType)
{
if (_measureConfig.Value.CheckIfMeasureExists)
{
Expand Down

0 comments on commit 2ede62e

Please sign in to comment.