The ResourceBuilder
object is used to construct FHIR resources.
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:
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';
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" ]
} ]
}
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';
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.
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.
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.
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.
When working with a Bundle resources, the following special methods exist:
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);
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;
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);
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
}
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
}
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);