Smile CDR v2024.05.PRE
On this page:

38.9.1FHIR Model API

 

The ResourceBuilder object is used to construct FHIR resources.

38.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';

38.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" ]
  } ]
}

38.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';

38.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.

38.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.

38.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.

38.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.

38.9.5Working with Bundle resources

 

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

38.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);

38.9.6Contained 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;

38.9.6.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);

38.9.6.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
}

38.9.7Meta 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
}

38.9.8Removing Data

 

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();

Contained data can be removed by calling splice(index, 1) e.g.:

theObservation.contained.splice(index, 1);