40.9.1FHIR Model API

 

The ResourceBuilder object is used to construct FHIR resources.

40.9.2Method: ResourceBuilder.build(resourceType)

 

This method creates a new instance of a FHIR resource of the given type, which can then be populated as necessary.

Inputs:

  • resourceType – The FHIR resource type (e.g. Patient or Observation).

Outputs:

  • Returns a resource instance that can be populated.

Example:

var patient = ResourceBuilder.build('Patient');

// Identifier (note that fields are created on access)
patient.identifier[0].system = 'http://acme.org/mrn';
patient.identifier[0].value = '1002837';

// Name (note that fields are created on access)
patient.name[0].family = 'Smith';
patient.name[0].given[0] = 'John';
patient.name[0].given[1] = 'Edward';

40.9.3Working with Resources

 

The following section describes features that are available when working with resources from within the scripting environment. Many of these examples show a resource being created using the ResourceBuilder utility. Note that the same functionality is available for any resources passed into a function, e.g. within an interceptor.

You can always check the type of a resource using the resourceType property. For example:

if (theResource.resourceType === 'Patient') {
   // do something
}

When populating fields in a resource, fields and elements will be auto-created as required. Generally, working with a resource is as simple as navigating the object hierarchy. Child objects and array offsets are created upon access, meaning that the following will not throw any exception.

var patient = ResourceBuilder.build('Patient');
patient.identifier[2].system = 'http://acme.org/mrn';
patient.identifier[1].system = 'http://example.com/mrn';

This same syntax may also be used to read property values. For example, the following code will copy the patient's family name from the first repetition to the second repetition of Patient.name:

var patient = ResourceBuilder.build('Patient');
var familyName = patient.name[0].family;
patient.name[1].family = familyName;

In the case of repeating elements, the use of an array offset is mandatory.

var givenName = patient.name.given; // FAILS
var givenName = patient.name[0].given[0]; // Succeeds

Elements also support an operator called add(fieldName) which adds a new repetition of the given element at the last index. For example:

// Add an instance of the field called 'name' on the Patient
// resource (this returns a FHIR HumanName datatype)
var name = patient.add('name');

// Populate that HumanName
name.family = 'Smith';
name.add('given') = 'John'; // add and populate
name.add('given') = 'Edward';

If you want to log a JSON representation of a resource, you can do so with .toJson(). This can be useful during development but should never be used in a production environment with real data.

Log.info("Patient:\n" + patient.toJson());

This would log something like the following to the system logs:

19:46:28.509 [TEST_MODULE_ID-0-1] INFO  JS.TEST_NODE_ID.TEST_MODULE_ID Log.java:46 - Patient:
{
  "resourceType": "Patient",
  "name": [ {
    "family": "Please don't log",
    "given": [ "PHI/PII" ]
  } ]
}

40.9.3.1Adding Extensions

A convenience method called addExtension(url) exists on any FHIR element that is able to accept an extension.

var patient = ResourceBuilder.build('Patient');

// Add an extension with the given url
var extension = patient.addExtension('http://example.com/eye-colour');

// Populate the value[x] field of the extension with a code datatype
extension.valueCode = 'blue';

Note that the value field on the extension is called value[x]. Per FHIR rules, in an instance, this becomes value followed by the name of the datatype. The following shows a few examples of how to populate different datatypes into an extension (although it would be an error to populate more than one datatype).

extension.valueString = 'Hello';

extension.valueDateTime = '2011-01-10T12:12:00Z';

extension.codeableConcept.coding[0].system = 'http://unitsofmeasure.org/';
extension.codeableConcept.coding[0].code = 'mL';
extension.codeableConcept.coding[0].display = 'Millilitres';

40.9.3.2Getting Extensions by URL

Similarly, a convenience method called getExtension(url) exists on any FHIR element that is able to accept an extension.

// Get an extension with the given url
var extension = patient.getExtension('http://example.com/eye-colour');

// Parse the value[x] field of the extension with a code datatype
var value = extension.valueCode;

Note again that the value field on the extension is called value[x]. Per FHIR rules, in an instance, this becomes value followed by the name of the datatype.

40.9.3.3Adding and Getting Extensions by URL for Primitive Types

When adding and getting extensions with primitive types, the element name should be prefixed with an underscore (i.e. _).

var patient = ResourceBuilder.build('Patient');

// Populate a primitive type
patient.name[0].family = 'Simpson';

// Add an extension on a primitive type with the given url
var addExtension = patient.name[0]._family.addExtension('http://example.com/also-known-as');

// Populate the value[x] field of the extension with a string datatype
addExtension.valueString = 'Bouvier';

// Get an extension on a primitive type with the given url
var getExtension = patient.name[0]._family.addExtension('http://example.com/also-known-as');

// Parse the value[x] field of the extension with a string datatype
var value = getExtension.valueString;

Note again that the value field on the extension is called value[x]. Per FHIR rules, in an instance, this becomes value followed by the name of the datatype.

40.9.3.4Looping over Elements

Many elements in FHIR are designed to be repeatable (in FHIR terms, they have a cardinality greater than 0..1). There are two styles that can be used to conveniently loop over these elements. First, the for..in style:

var familyNames = new Array();
for (var index in theBundle.entry) {
   var nextPatient = theBundle.entry[index].resource;
	familyNames.push(nextPatient.name.family);
}

Second, the forEach style:

var familyNames = new Array();
theBundle.entry.forEach(function(nextEntry) {
   var nextPatient = nextEntry.resource;
	familyNames.push(nextPatient.name.family);
});

Note that for simplicity, these techniques may also be used for non-repeating elements.

40.9.4Working with Composition resources

 

If you use build('Composition'), the resultant Composition resource will have additional functionality beyond that of a resource of any other type created with this method. For information about that added functionality, see Composition Resource API.

40.9.5Working with Bundle resources

 

When working with a Bundle resources, the following special methods exist:

40.9.5.1Method: entryResources() and entryResources(resourceType)

This method can be used to retrieve an array of the resources in the Bundle within the field Bundle.entry.resource. Optionally you can pass a String parameter indicating the resource type you want.

For example, the following statements retrieve all resources in the Bundle:

var allResources = bundle.entryResources();
var allPatients = bundle.entryResources('Patient');

To add an entry to an existing bundle, you can use the addEntry() method. For example:

let prov = ResourceBuilder.build('Provenance');
prov.target[0].reference = 'Patient/123';
theBundle.addEntry().setResource(prov);

40.9.6Resource References

 

There are a couple of different ways to add or update references inside a FHIR resource using the FHIR Model API.

40.9.6.1Adding Reference Element

To add a reference that points to a new resource created in the current script execution or to a contained resource, the recommended approach is to set the Reference element to the value of the new resource or contained resource. For example:

let organization = ResourceBuilder.build('Organization');
organization.name = 'Friendly Clinic';
let patient = ResourceBuilder.build('Patient');
patient.managingOrganization = organization;

40.9.6.2Adding Reference.reference Child Element

If adding a reference that points to a resource that is known to exist already in the target repository, you can alternatively just set the Reference.reference child element to a String value containing the relative, internal, or absolute URL for the referenced resource. For example:

let patient = ResourceBuilder.build('Patient');
patient.managingOrganization.reference = 'Organization/123';

40.9.7Contained Resources

 

To create a contained resource, simply create a resource but do not assign it an ID, and then assign it directly to the reference element in the parent resource. This is illustrated in the example below.

Note that this only populates the reference inside the parent resource. Use of the TransactionBuilder API ensures the contained resource is actually added to the contained element of the parent resource (see below).

var observation = ResourceBuilder.build('Observation');

// Create a specimen that will be contained
var specimen = ResourceBuilder.build('Specimen');

// Set some fields on the specimen
specimen.status = 'available';

// Assign the resource to the reference
observation.specimen = specimen;

40.9.7.1Adding Contained Resources without the TransactionBuilder API

Occasionally, it is necessary to add a contained resource to an existing Bundle. For example, an HL7 v2.x postConvert script exposes a transaction Bundle prior to processing the Bundle. In this case, there is no need for the script to make use of the TransactionBuilder API

When adding one or more contained resources without using the TransactionBuilder API, assign the references as usual, and then additionally invoke the .addContained(resource) function against the parent resource for any contained resources.

The following example shows a contained Organization and a contained Practitioner being both assigned and added to a parent Patient resource.

// Create parent resource
let patient = ResourceBuilder.build('Patient');

// Create and set data inside of resources to be contained
let org = ResourceBuilder.build('Organization);
org.name = 'University Hospital';

let prac = ResourceBuilder.build('Practitioner');
prac.name[0].family = 'Smith';
prac.name[0].given[0] = 'Rob';

// Assign the resources to the references
patient.generalPractitioner[0] = org;
patient.generalPractitioner[1] = prac;
patient.managingOrganization = org;

// Call on the .addContained() method against the parent resource
patient.addContained(org);
patient.addContained(prac);

40.9.7.2Checking for the existence of a Contained Resource

To check if a resource is contained within another resource, use the hasContained() method. This method returns true if the resource has any contained resources, and false otherwise.

function hasContainedResources(theResource) {
   return theResource.hasContained(); // returns true if the resource has contained resources
}

40.9.7.3Removing or replacing Contained Resources

In some cases, Smile CDR will automatically create contained resources, e.g. as part of the HL7 v2.x inbound message processing. In these cases there may be a need to remove the contained resource or to replace the reference with one that points to a different, possibly non-contained resource. There are two ways that this can be accomplished.

If the new target resource is available in the script, e.g. a new resource being created or an existing resource read from the repository, you can simply overwrite the reference using the new resource. For example:

// A new organization resource
let organization = ResourceBuilder.build('Organization');
organization.name = 'Friendly Clinic';

// Update payor for existing Coverage resource
coverage.payor[0] = organization;

If only a URI reference for the new target resource is available, it will be necessary to first remove the existing reference to the contained resource as well as the contained resource itself before you will be able to overwrite the reference. See Removing Data for more information about removing references and contained resources. For example:

// A new organization reference
let organization_reference = 'Organization/123';

// Remove the first payor reference in existing Coverage resource
coverage.payor.split(0,1);
// Remove the contained resource that was being referenced by the Coverage resource
coverage.contained.split(0,1);

// Update payor for Coverage resource
coverage.payor[0].reference = organization_reference;

40.9.8Meta Elements

 

You can test whether a resource has a particular tag, security label, or profile using the following utility functions:

if (thePatient.meta.hasTag('http://system', 'value')) {
	// do something
}
if (thePatient.meta.hasSecurity('http://system', 'value')) {
	// do something
}
if (thePatient.meta.hasProfile('http://system.com|1.0.0')) {
   // do something
}

40.9.9Patient Deceased Element

 

The Patient.deceased[x] element can be of type BooleanType or DateTimeType. To determine element type before accessing it, we have provided the following two methods:

if (thePatient.hasDeceasedBooleanType()) {  // returns true if element deceased is of type boolean, false if null or of type DateTime
	if (thePatient.deceasedBoolean) {
		// do something when the patient is deceased
	}
}

if (thePatient.hasDeceasedDateTimeType()) {  // returns true if element deceased is of type DateTime, false if null or of type Boolean
	let deceasedDateTime = thePatient.deceasedDateTime; 
	// do something
}

40.9.10Removing Data

 

40.9.10.1clear(fieldName) method

Data can be cleared from a field by calling clear('fieldName'), e.g.:

theObservation.clear('value');
theObservation.clear('comment');
theObservation.referenceRange[0].clear('type');

An object can also have all of its data cleared by calling clear(), e.g.:

theObservation.valueQuantity.clear();

40.9.10.2splice(index,count)

Data values in an element that has cardinality greater than 1, can be removed by calling splice(index, count) where index would be set to the index of the first value to be removed and count would be set to the number of consecutive values to be removed from the element starting from the specified index. For example:

// Remove the first contained resource from Observation resource
theObservation.contained.splice(0, 1);

// Remove SSN identifier from Patient resource (which happens to be the third identifier for the Patient).
let SSN_index = 2;
thePatient.identifier.splice(SSN_index,1);