28.5.1Custom Outbound Resource Conversion
Experimental

 
This type of resource conversion is marked as experimental as the API is still being developed and may change as we receive feedback.

Custom Outbound Resource Conversion relies on a custom Java-based message converter class that you define. This converter class can take advantage of Smile CDR's built-in message conversion routines if you want, but it can also use individual segment converters or rely completely on custom conversion. As a result, it can create any HL7 v2.x message type and is very flexible.

It relies on a FHIR Subscription to trigger messages. This can be a subscription on business data resources as is the case with Default Outbound Resource Conversion but it can also rely on MessageHeader resources, which can be useful if you want your outbound HL7 v2.x messages to correspond to real-world business workflows as opposed to data-level changes.

28.5.2Business Transaction Triggers with MessageHeader

 

If you are designing a solution where you are performing storage actions as a part of a larger business transaction, and you would like that business transaction to dictate the type and contents of the generated HL7 v2.x message, using a MessageHeader resource can be a good option.

For example, suppose you have a system which often stores Encounter resources, and you want these to generate either an ADT^A01, ADT^A04, or ADT^A08 message depending on the business context. In this case, you can use a FHIR Transaction to store both the Encounter and any other resources relevant to the workflow. In addition to these resources, you can also store a new MessageHeader resource which will ultimately trigger the outbound message. This MessageHeader resource is passed to your custom mapper class, which uses it to determine which type of message to send and what it should contain.

The MessageHeader.focus element is used to signal the relevant FHIR resources for the given transaction. If you include versioned references here, you can also ensure that the correct version of these resources are used to generate the outbound message which is useful if you expect frequent updates to resources and want your HL7 v2.x messages to accurately represent the complete history of changes to data in order. Enabling Automatically Version References on this field can help to make this versioning automatic as a part of your FHIR transactions.

A simple example MessageHeader is shown below. Note that this is just an example, and the contents and format are complete up to you since your converter class is fully responsible for interpreting these values.

 {
  "resType": "MessageHeader",
  "eventCoding": {
    "system": "http://terminology.hl7.org/CodeSystem/v2-0003",
    "code": "A01"
  },
  "focus": [ {
    "reference": "Patient/123/_history/5"
  }, {
    "reference": "Encounter/555/_history/2"
  } ]
}

28.5.3Custom Mapper Class

 

The custom mapper class is a class you create, which implements the IHl7V2OutboundMapperSvc interface.

An example is shown below:

public class MessageHeaderCustomMessageMapperExample implements IHl7V2OutboundCustomMapper {

    @Override
    public List<MappingTarget<?>> convert(ConversionContext theConversionContext) throws HL7Exception {
        IHl7V2OutboundMapperSvc outboundMapperSvc = theConversionContext.getHl7V2OutboundMapperSvc();
        MessageHeader focalMessageHeader = (MessageHeader) theConversionContext.getFocalResource();

        // Create a mapping instructions file - this can be used to supply settings
        // to the built-in Smile conversion algorithms. Here we will apply versions
        // from the MessageHeader, meaning that if we need to resolve any resources
        // in the process of applying Smile default mappings, we will use the specific
        // versions called for in the MessageHeader
        OutboundMappingInstructions mappingInstructions;
        mappingInstructions = new OutboundMappingInstructions.Builder()
                .addSourceDataResourceVersionsFromMessageHeaderFocus(focalMessageHeader)
                .build();

        MappingTarget<ADT_A05> mappingTarget = outboundMapperSvc.newTarget("ADT", "A28", ADT_A05.class);

        // Resolve the Encounter using the reference found in the MessageHeader
        Encounter encounter = theConversionContext.resolveFirstResourceFromMessageHeaderFocus(Encounter.class, focalMessageHeader);

        // In this example we'll use the default Smile CDR algorithm to populate
        // the entire message
        outboundMapperSvc.populateMessageAdtA05FromEncounter(encounter, mappingTarget, mappingInstructions);

        // We can also add additional mappings if we want
        mappingTarget.getMessage().getUB1().getUb11_SetIDUB1().setValue("1");
        mappingTarget.getMessage().getUB1().getUb12_BloodDeductible().setValue("22");

      // We're always returning one message here, but the converter could technically
      // produce multiple messages or even zero messages.
      return List.of(mappingTarget);
    }
}

You can also manually construct messages using individual segment converters only, and skip the complete built-in message conversion routine, as shown in the following example:

public class MessageHeaderCustomSegmentMapperExample implements IHl7V2OutboundCustomMapper {

    @Override
    public List<MappingTarget<?>> convert(ConversionContext theConversionContext) throws HL7Exception {
        IHl7V2OutboundMapperSvc outboundMapperSvc = theConversionContext.getHl7V2OutboundMapperSvc();
        MessageHeader focalMessageHeader = (MessageHeader) theConversionContext.getFocalResource();

        // Create a mapping instructions file - this can be used to supply settings
        // to the built-in Smile conversion algorithms. Here we will apply versions
        // from the MessageHeader, meaning that if we need to resolve any resources
        // in the process of applying Smile default mappings, we will use the specific
        // versions called for in the MessageHeader
        OutboundMappingInstructions mappingInstructions;
        mappingInstructions = new OutboundMappingInstructions.Builder()
                .addSourceDataResourceVersionsFromMessageHeaderFocus(focalMessageHeader)
                .build();

        MappingTarget<ADT_A01> mappingTarget = outboundMapperSvc.newTarget("ADT", "A01", ADT_A01.class);
        ADT_A01 message = mappingTarget.getMessage();

        // Resolve the Patient and Encounter using the reference found in the MessageHeader
        Patient patient = theConversionContext.resolveFirstResourceFromMessageHeaderFocus(Patient.class, focalMessageHeader);
        Encounter encounter = theConversionContext.resolveFirstResourceFromMessageHeaderFocus(Encounter.class, focalMessageHeader);

        // In this example we'll use the default Smile CDR algorithm to populate
        // the PID and PV1 segments
        outboundMapperSvc.populateSegmentPidFromPatient(patient, encounter, message.getPID(), mappingTarget, mappingInstructions);
        outboundMapperSvc.populateSegmentPv1FromEncounter(encounter, message.getPV1(), mappingTarget, mappingInstructions);

        // We'll also manually populate the EVN segment. Other segments could be
        // populated in the same way
        EVN evn = message.getEVN();
        evn.getEvn1_EventTypeCode().setValue("A01");

      // We're always returning one message here, but the converter could technically
      // produce multiple messages or even zero messages.
      return List.of(mappingTarget);
    }
}

28.5.4Custom Mapper Subscription

 

An example Subscription is shown below:

{
  "resourceType": "Subscription",
  "id": "SUBSID",
  "status": "requested",
  "criteria": "DiagnosticReport?",
  "channel": {
    "extension": [
      {
        "url": "https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-rest-delivery-class",
        "valueString": "ca.cdr.endpoint.hl7v2.out.deliverer.OutboundHl7V2SubscriptionDeliverer"
      },
      {
        "url": "https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-hl7v2-sender-module-id",
        "valueString": "hl7_sender"
      },
      {
        "url": "https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-hl7v2-custom-mapper-class",
        "valueCode": "org.example.MessageHeaderCustomSegmentMapperExample"
      }
    ],
    "type": "rest-hook"
  }
}

The subscription-channel-hl7v2-custom-mapper-class extension contains the fully-qualified class name of your custom mapper class.