Files
vigilcare-clinical/VigilCareClinicalAPI/Controllers/FhirIngestController.cs
T

150 lines
6.3 KiB
C#

using Hl7.Fhir.Model;
using Hl7.Fhir.Serialization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
[ApiController]
[Route("fhir/R4")]
[ServiceFilter(typeof(FhirExceptionFilter))]
public class FhirIngestController : ControllerBase
{
private static readonly FhirJsonParser Parser = new();
private static readonly FhirJsonSerializer Serializer = new();
private readonly IPatientService _patients;
private readonly IEncounterService _encounters;
private readonly IObservationService _observations;
private readonly IMedicationService _medications;
private readonly IExternalIdentifierService _identifiers;
private readonly PatientFhirMapper _patientMapper;
private readonly EncounterFhirMapper _encounterMapper;
private readonly ObservationFhirMapper _observationMapper;
private readonly MedicationAdministrationFhirMapper _medMapper;
private readonly FhirBundleProcessor _bundleProcessor;
private readonly FhirOptions _options;
private readonly ClinicalMetrics _metrics;
public FhirIngestController(
IPatientService patients,
IEncounterService encounters,
IObservationService observations,
IMedicationService medications,
IExternalIdentifierService identifiers,
PatientFhirMapper patientMapper,
EncounterFhirMapper encounterMapper,
ObservationFhirMapper observationMapper,
MedicationAdministrationFhirMapper medMapper,
FhirBundleProcessor bundleProcessor,
IOptions<FhirOptions> options,
ClinicalMetrics metrics)
{
_patients = patients;
_encounters = encounters;
_observations = observations;
_medications = medications;
_identifiers = identifiers;
_patientMapper = patientMapper;
_encounterMapper = encounterMapper;
_observationMapper = observationMapper;
_medMapper = medMapper;
_bundleProcessor = bundleProcessor;
_options = options.Value;
_metrics = metrics;
}
[HttpPost("Patient")]
[Consumes("application/fhir+json")]
[Produces("application/fhir+json")]
public async Task<IActionResult> CreatePatient()
{
var fhir = await ParseBodyAsync<Hl7.Fhir.Model.Patient>();
var req = _patientMapper.ToUpsertRequest(fhir);
var patient = await _patients.RegisterOrUpdateByIdentifierAsync(req);
var hospitalId = await _identifiers.FindPrimaryIdentifierAsync(
ExternalResourceType.Patient, patient.Id, _options.PatientIdentifierSystems);
var response = _patientMapper.ToFhirResponse(patient, hospitalId);
_metrics.FhirIngestTotal.WithLabels("Patient", "success").Inc();
return Created($"{Request.Path}/{patient.Id}", Serialize(response));
}
[HttpPost("Encounter")]
[Consumes("application/fhir+json")]
[Produces("application/fhir+json")]
public async Task<IActionResult> CreateEncounter()
{
var fhir = await ParseBodyAsync<Hl7.Fhir.Model.Encounter>();
var req = await _encounterMapper.ToUpsertRequestAsync(fhir);
var encounter = await _encounters.OpenOrUpdateByIdentifierAsync(req);
var hospitalId = await _identifiers.FindPrimaryIdentifierAsync(
ExternalResourceType.Encounter, encounter.Id, _options.EncounterIdentifierSystems);
var response = _encounterMapper.ToFhirResponse(encounter, hospitalId);
_metrics.FhirIngestTotal.WithLabels("Encounter", "success").Inc();
return Created($"{Request.Path}/{encounter.Id}", Serialize(response));
}
[HttpPost("Observation")]
[Consumes("application/fhir+json")]
[Produces("application/fhir+json")]
public async Task<IActionResult> CreateObservation()
{
var fhir = await ParseBodyAsync<Hl7.Fhir.Model.Observation>();
var mapped = await _observationMapper.ToIngestRequestsAsync(fhir);
Resource? lastResponse = null;
foreach (var item in mapped)
{
var result = await _observations.IngestAsync(item.EncounterId, item.Request);
lastResponse = new Hl7.Fhir.Model.Observation
{
Id = result.Observation.Id.ToString(),
Status = ObservationStatus.Final,
Code = new CodeableConcept("http://loinc.org", item.Request.ObservationCode),
Value = new Quantity(item.Request.Value, item.Request.Unit)
};
}
_metrics.FhirIngestTotal.WithLabels("Observation", "success").Inc();
return Created(Request.Path.Value!, Serialize(lastResponse!));
}
[HttpPost("MedicationAdministration")]
[Consumes("application/fhir+json")]
[Produces("application/fhir+json")]
public async Task<IActionResult> CreateMedicationAdministration()
{
var fhir = await ParseBodyAsync<Hl7.Fhir.Model.MedicationAdministration>();
var (req, encounterId) = await _medMapper.ToCreateRequestAsync(fhir);
var med = await _medications.CreateAsync(encounterId, req);
var response = new Hl7.Fhir.Model.MedicationAdministration { Id = med.Id.ToString() };
_metrics.FhirIngestTotal.WithLabels("MedicationAdministration", "success").Inc();
return Created($"{Request.Path}/{med.Id}", Serialize(response));
}
/// <summary>Accepts Bundle.type=transaction (ADT admit) or batch.</summary>
[HttpPost]
[Consumes("application/fhir+json")]
[Produces("application/fhir+json")]
public async Task<IActionResult> ProcessBundle()
{
using var reader = new StreamReader(Request.Body);
var json = await reader.ReadToEndAsync();
var bundle = Parser.Parse<Bundle>(json);
if (bundle.Type != Bundle.BundleType.Transaction)
throw new FhirMappingException("Only transaction Bundles are supported.", "not-supported");
var responseBundle = await _bundleProcessor.ProcessTransactionAsync(bundle);
_metrics.FhirIngestTotal.WithLabels("Bundle", "success").Inc();
return Ok(Serialize(responseBundle));
}
private async Task<T> ParseBodyAsync<T>() where T : Resource
{
using var reader = new StreamReader(Request.Body);
var json = await reader.ReadToEndAsync();
return Parser.Parse<T>(json);
}
private ContentResult Serialize(Resource resource) =>
Content(Serializer.SerializeToString(resource), "application/fhir+json");
}