Skip to content

Latest commit

 

History

History
145 lines (93 loc) · 6.35 KB

File metadata and controls

145 lines (93 loc) · 6.35 KB

Constructing bundles

In a Plugin or Facade you may need to construct a bundle from a set of resources, e.g. a SearchResult (see :ref:`here <vonk_reference_api_isearchrepository>`). There are two ways of doing this: with the Bundle POCO or with SourceNodes.

Bundle POCO

This is fairly straightforward. Create a new Bundle object, and fill its properties, iterating over the set of resources that you have. The code looks like this:

using Hl7.Fhir.Model; //Either from Hl7.Fhir.Core.Stu3 or Hl7.Fhir.Core.R4

...

   var searchResult = await searchRepository.Search(args, options)
   var bundle = new Bundle() { Type = Bundle.BundleType.Searchset }; // Type is required

   foreach (var resource in searchResult)
   {
      bundle.Entry.Add
      {
         new BundleEntryComponent
         {
            Resource = resource.ToPoco<Resource>();
            // fill in any other details like Request or Response.
         }
      }
   }

   //fill in details of the bundle as a whole, like Meta or Identifier.

The limitation of this is that you are bound to either STU3 or R4, and that also implies that you cannot include :ref:`feature_customresources` in the bundle.

Bundle from SourceNodes

ISourceNode is from Hl7.Fhir.ElementModel and not tied to a specific FHIR version, and Firely Server can serialize ISourceNode provided that the right StructureDefinition is available in the Administration API - which is the case for Bundle by default.

You start by creating the Bundle itself using the class SourceNode that allows for construction of ISourceNode nodes.

using Hl7.Fhir.ElementModel;

...

   var bundleNode = SourceNode.Resource("Bundle", "Bundle", SourceNode.Valued("type", "document"));

   //choose type as one of the bundle types from the spec, see http://hl7.org/fhir/R4/bundle-definitions.html#Bundle.type

Then you can add elements to the bundle itself that are not in the entries of the bundle. Like an identifier:

var identifier = SourceNode.Node("identifier");
identifier.Add(SourceNode.Valued("system", "urn:ietf:rfc:3986"));
identifier.Add(SourceNode.Valued("value", Guid.NewGuid().ToString()));
bundleNode.Add(identifier);

Then you can turn this into the helper class GenericBundle that provides several helper methods on an ISourceNode that is known to be a Bundle.

var documentBundle = GenericBundle.FromBundle(bundleNode);
documentBundle = documentBundle.Meta(Guid.NewGuid().ToString(), DateTimeOffset.Now);

Maybe you already saw an alternative way of adding the identifier in the intellisense by now:

documentBundle = documentBundle.Identifier("urn:ietf:rfc:3986", Guid.NewGuid().ToString());

Note that you always have to continue with the result of the modifying function. All these functions act on ISourceNode and that is immutable, so you get a new instance with the changes applied as a return value.

Now you have the skeleton of the Bundle, it is ready to add entries with resources to it.

IResource resourceForDocument = ... ; //Get or construct a resource that is one of the entries of the Bundle.
documentBundle = documentBundle.AddEntry(resourceForDocument, resourceForDocument.Key().ToRelativeUri());

Other extensions methods available on GenericBundle:

public static GenericBundle Total(this GenericBundle bundle, int total)
public static GenericBundle AddLink(this GenericBundle bundle, string relation, string uri)
public static GenericBundle Links(this GenericBundle bundle, Dictionary<string, string> links)

Search result bundles

Usually you don't need to construct a searchset bundle yourself, since the SearchService takes care of that when a search is issued on the FHIR endpoint. But should you want to do it in a custom operation, then the methods for doing so are at your disposal.

To help construct a bundle of type 'searchset', there is a special kind of bundle class SearchBundle. Create the sourcenode for the bundle as above. Then instead of creating a GenericBundle, turn it into a SearchBundle:

var searchBundle = bundleNode.ToSearchBundle();

Now you can use various methods to add entries for matches, includes or an OperationOutcome:

//SearchBundle methods
public SearchBundle AddMatch(ISourceNode resource, string fullUrl, string score = null)
public SearchBundle AddInclude(ISourceNode resource, string fullUrl, string score = null)
public SearchBundle AddOutcome(ISourceNode outcome, string fullUrl, string score = null)

//Extension methods
public static SearchBundle ToSearchBundle(this IEnumerable<SearchInfo> searchInfos, string informationModel)
public static SearchBundle ToSearchBundle(this IEnumerable<ISourceNode> resources, string searchMode, string informationModel)
public static SearchBundle ToSearchBundle(this IEnumerable<ITypedElement> resources, string searchMode, string informationModel)

The SearchInfo struct essentially captures all the information that goes into an entry of a searchset bundle:

public struct SearchInfo
{
   public SearchInfo(ISourceNode resource, string mode = SearchMode.match, string fullUrl = null, string score = null)

   public string Mode { get; }
   public ISourceNode Resource { get; }
   public string FullUrl { get; }
   public string Score { get; }
}

Using all this to turn the SearchResult returned from the ISearchRepository.Search() method into a bundle looks like this (using the second extension method above):

var bundle = searchResult
      .ToSearchBundle(SearchMode.match, vonkContext.InformationModel)
      //informationModel is needed because bundle has slight differences between STU3 and R4
      .Total(searchResult.Page.TotalCount)
      //Total is defined on GenericBundle
      .Links(searchResult.Page.PagingLinks(vonkContext));
      //Links is defined on GenericBundle
return bundle.ToIResource(vonkContext.InformationModel).EnsureMeta();