Enable changes to the repository
In Exercise: Build your first Facade you have created read and search support for a Firely Server Facade on top of an existing database. The next part will walk you through enabling create, update and delete support. This will be done in three steps:
Map the FHIR resource to the database model;
Implement the IResourceChangeRepository;
Indicate support in the appsettings file
1. Map the FHIR data to the model
For both the Create and Update interactions, we need to map our incoming FHIR data to the ViSi model. To do that, we will add new mapping methods to our ResourceMapper class.
Add a method called
MapVisiPatient
to the ResourceMapper class, that takes a FHIRPatient
and returns aViSiPatient
.Implement the mapping from the FHIR object to the ViSiPatient model:
public ViSiPatient MapViSiPatient(IResource source) { var fhirPatient = source.ToPoco<Patient>(); var visiPatient = new ViSiPatient(); if (source.Id != null) { if (!int.TryParse(source.Id, out int id)) throw new VonkRepositoryException("Id needs to be integer to map resource"); visiPatient.Id = id; } visiPatient.PatientNumber = fhirPatient.Identifier.Find(i => (i.System == "http://mycompany.org/patientnumber")).Value; // etc. return visiPatient; }
Where it says ‘etc.’, fill in the rest of the code to map the data to required fields of the database, and any other fields you have data for.
2. Implement the IResourceChangeRepository
You are going to implement a repository that handles changes to your database. The interface for this is called IResourceChangeRepository
, which
can be found in Vonk.Core.Repository
.
Add a new class
ViSiChangeRepository
to the project, that implements the IResourceChangeRepository:public class ViSiChangeRepository : IResourceChangeRepository
Choose to implement the interface, so the required methods are added to the class.
Just like with the search repository, you will need your DbContext to query on, and the ResourceMapper to perform the mapping of the incoming data to your proprietary model.
So put all of that in the constructor:
private readonly ViSiContext _visiContext; private readonly ResourceMapper _resourceMapper; public ViSiChangeRepository(ViSiContext visiContext, ResourceMapper resourceMapper) { _visiContext = visiContext; _resourceMapper = resourceMapper; }
Implementing Create
Now implement the Create method with a switch on resource type, so you can add other resource types later:
public async Task<IResource> Create(IResource input) { switch (input.Type) { case "Patient": return await CreatePatient(input); default: throw new NotImplementedException($"ResourceType {input.Type} is not supported."); } }
As you can see, we have deferred the work to a CreatePatient method, which we also need to implement. This method will add the new resource to the collection, and save the changes to the database:
private async Task<IResource> CreatePatient(IResource input) { var visiPatient = _resourceMapper.MapViSiPatient(input); await _visiContext.Patient.AddAsync(visiPatient); await _visiContext.SaveChangesAsync(); // return the new resource as it was stored by this server return _resourceMapper.MapPatient(_visiContext.Patient.Last()); }
For the
Create
andUpdate
methods, you will also need to implement theNewId
andNewVersion
methods, because Firely Server will call them. For theNewId
method, we will return null, since our ViSi database does not allow us to create our own index value. Since our ViSi repository does not handle versions, we will let theNewVersion
method return null as well:public string NewId(string resourceType) { return null; } public string NewVersion(string resourceType, string resourceId) { return null; }
Note
For the ViSi repository we’re using a null value, but you can implement this method any way that’s useful for your own repository. The public Firely Server for example generates a GUID in these methods.
At this point you can skip ahead to 3. Configure the service and Firely Server, if you want to try and create a new patient in the ViSi database.
Tip
This is easiest to test if you retrieve an existing resource from the database first with your HTTP tool. Then change some of the data in the resulting JSON or XML, and send that back to your Facade.
Implementing Update
Implementing the Update
method can be done like the Create
, with a switch on resource type, and instead of adding
a resource to the collection, you will update the collection:
private async Task<IResource> UpdatePatient(ResourceKey original, IResource update)
{
try
{
var visiPatient = _resourceMapper.MapViSiPatient(update);
var result = _visiContext.Patient.Update(visiPatient);
await _visiContext.SaveChangesAsync();
return _resourceMapper.MapPatient(result.Entity);
}
catch (Exception ex)
{
throw new VonkRepositoryException($"Error on update of {original} to {update.Key()}", ex);
}
}
Implementing Delete
Deleting a resource from the collection is done by first looking up the corresponding resource, and then removing it from the collection. Note that the database used for this exercise cannot process the deletion of the Patient when there are still related Observations in the BloodPressure table, so we need to remove them as well or choose to throw an error.
First, create a switch on resource type in the main
Delete
method again.Implement the
DeletePatient
:private async Task<IResource> DeletePatient(ResourceKey toDelete) { int toDelete_id = int.Parse(toDelete.ResourceId); var visiPatient = _visiContext.Patient.Find(toDelete_id); var bpEntries = _visiContext.BloodPressure.Where(bp => bp.PatientId == toDelete_id); var result = _resourceMapper.MapPatient(visiPatient); try { _visiContext.BloodPressure.RemoveRange(bpEntries); _visiContext.Patient.Remove(visiPatient); await _visiContext.SaveChangesAsync(); } catch (Exception ex) { throw new VonkRepositoryException($"Error on deleting Patient with Id {toDelete_id}", ex); } return result; }
3. Configure the service and Firely Server
Just like with the search repository, you will need to add your change repository as service to the pipeline. Also, you will need to indicate support for the CRUD interactions in your Firely Server appsettings.
In your project, go to the ViSiConfiguration class, and add this line to add an IResourceChangeRepository to the pipeline:
services.TryAddScoped<IResourceChangeRepository, ViSiChangeRepository>();
Add support for the interactions to the SupportedModel section of the Firely Server appsettings:
"SupportedInteractions": { "InstanceLevelInteractions": "read, update, delete", "TypeLevelInteractions": "search, create", "WholeSystemInteractions": "capabilities, search" },
Adjust
PipelineOptions.Branches.Include
fromVonk.Plugin.Operations.Crud.ReadConfiguration
to``Vonk.Plugin.Operations
include all operations, includingCreate
.
You can now build your project, copy the dll to the Firely Server plugins folder and run Firely Server to test the new interactions on your Facade.
The end?
This concludes the second exercise. Please feel free to try out more options, and ask for help if you get stuck!