JavaScript Hooks on CDA Import / Export
CDA exchange allows consumers to supply JavaScript hooks that get executed before and after import and after export.
This allows users to alter/add additional information to documents before processing.
Below are some examples of the scripts that can be provided.
To provide a pre import hook, you need to supply JavaScript that includes the function
onPreImportCDA
.
If supplied, the function will be invoked with a parameters
object that contains the input document (as a text string).
If supplied, this function must return the resulting XML document to process, even if no changes are made!
See below for an example of the onPreImportCDA
handler.
To provide a post import hook, you need to supply JavaScript that includes the function
onPostImportCDA
.
If supplied, the function will be invoked with a parameters
object with accessors to retrieve the created bundle, the operation outcome, and the supplied document (as a text string).
See the examples below for how to utilize the onPostImportCDA
handler.
To provide a post export hook, you need to supply JavaScript that includes the function
onPostExportCDA
.
If supplied, the function will be invoked with a parameters
object with accessors to retrieve the bundle and the created CDA document (provided as an XML string).
If supplied, this function must return the resulting XML document to save, even if no changes were made!
See the examples below for how to utilize the onPostExportCDA
handler.
For information on APIs in the JavaScript execution environment, refer to the standard APIs exposed to the JavaScript environment.
Because CDA utilizes XML documents in the JavaScript hooks, it may be useful to read up on the exposed XML API.
Examples of various JavaScript hooks.
Each example will be defined individually, but all handlers can be put into the same script.
Example 1: Modify the input document to append a new element. Return the newly constructed document.
function onPreImportCDA(params) {
const xmlString = params.getDocument();
const document = XML.createDocument(xmlString);
const patientNode = document.getXPathElements("/ClinicalDocument/recordTarget/patientRole/patient")[0];
// create new element
const ethnicGroupEl = document.createNewElement('sdtc:ethnicGroupCode');
ethnicGroupEl.setOrAddAttribute("code", "2137-8");
ethnicGroupEl.setOrAddAttribute("codeSystem", "2.16.840.1.113883.6.238");
ethnicGroupEl.setOrAddAttribute("codeSystemName", "Race & Ethnicity - CDC");
ethnicGroupEl.setOrAddAttribute("displayName", "Spaniard");
// add it to the appropriate spot in the xml
patientNode.appendChild(ethnicGroupEl);
// return the newly formed xml
return document.toXMLString();
}
Example 1: Modify HTTP Verbs on bundle entry requests from POST to GET
function onPostImportCDA(params) {
const bundle = params.getBundle();
const outcome = params.getOutcome();
const strDoc = params.getDocument();
if (bundle.entry && bundle.entry.length) {
const len = bundle.entry.length;
for (let i = 0; i < len; i++) {
const entry = bundle.entry[i];
if (entry.request && entry.request.method) {
if (entry.request.method == "POST") {
entry.request.method = "GET";
}
}
}
}
}
Example 2:
Parse sdtc:ethnicGroupCode
from initial XML document and add a sub-extension to the Bundle's Patient's us-core-ethnicity
extension with the relevant system
, code
, and display
.
/* Main javascript hook */
function onPostImportCDA(params) {
const docStr = params.getDocument();
const bundle = params.getBundle();
const patient = getPatientFromBundle(bundle);
if (!patient) {
console.error("Could not find patient!");
return;
}
// parse out the nodes we are interested in from the xml
const xmlDoc = XML.createDocument(docStr);
const nodes = xmlDoc.getXPathElements("/ClinicalDocument/recordTarget/patientRole/patient/sdtc:ethnicGroupCode");
// This node has a cardinality of 0..*
const len = nodes.length;
if (len) {
for (let i = 0; i < len; i++) {
const node = nodes[i];
const usCoreEthnicityExtension = patient.getExtension("http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity");
if (usCoreEthnicityExtension) {
ext = usCoreEthnicityExtension.addExtension("detailed");
const coding = ext.valueCoding;
// fill the coding in with the values from the node
populateCodingWithNodeValues(node, coding);
}
}
}
}
/* Helper function to extract patient from bundle */
function getPatientFromBundle(bundle) {
const resources = bundle.entryResources();
if (resources && resources.length) {
const len = resources.length;
for (let i = 0; i < len; i++) {
const resource = resources[i];
if (resource.resourceType == 'Patient') {
// there should be exactly 1 patient in the bundle
return resource;
}
}
}
// it should always find a patient
return void 0;
}
/* Helper function to populate coding system values from provided node */
function populateCodingWithNodeValues(node, coding) {
// returns arrays
// but cardinality of any given xml node attributes is 0..1
const codes = node.getXPathValues("@code");
const displays = node.getXPathValues("@displayName");
const codeSystems = node.getXPathValues("@codeSystem");
let code = "";
if (codes.length) {
code = codes[0];
}
let display = "";
if (displays.length) {
display = displays[0];
}
let codeSystem = "";
if (codeSystems.length) {
codeSystem = "urn:oid:" + codeSystems[0];
}
// set to coding system
coding.system = codeSystem;
coding.display = display;
coding.code = code;
}
Example 1:
Using the coding information on the extensions of the patient resource embedded on the bundle, create and append the stdc:ethnicGroupCode
node to the created XML document. Return the newly parsed XML for saving.
/** The CDA Post Export JavaScript hook */
function onPostExportCDA(params) {
// retrieve the bundle and the document from the parameters
const bundle = params.getBundle();
const cdaDoc = XML.createDocument(params.getDocument());
// fetch the patient off the bundle
const patient = getPatientFromBundle(bundle);
const usCoreEthnicityExtension = patient.getExtension("http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity");
/*
* If the ethnicity extension is found on the embedded patient
* in the bundle, we'll extract any subextension with the url 'detailed'
* and use it to add the equivalent node to the XML document.
*/
if (usCoreEthnicityExtension) {
const subExtension = usCoreEthnicityExtension.getExtension('detailed');
if (subExtension && subExtension.valueCoding) {
const coding = subExtension.valueCoding;
addEthnicGroupCodeToPatientNode(coding, cdaDoc);
}
}
// return the newly altered document for saving
return cdaDoc.toXMLString();
}
/**
* Retrieves the patient resource from the bundle
*/
function getPatientFromBundle(bundle) {
const resources = bundle.entryResources();
if (resources && resources.length) {
const len = resources.length;
for (let i = 0; i < len; i++) {
const resource = resources[i];
if (resource.resourceType == 'Patient') {
// there should be exactly 1 patient in the bundle
return resource;
}
}
}
// it should always find a patient
return void 0;
}
/**
* Retrieve the <patient> node, or create it if it doesn't exist,
* and then return it.
*/
function getPatientNodeOnDoc(cdaDocument) {
const paths = [
'ClinicalDocument',
'recordTarget',
'patientRole',
'patient'
];
const len = paths.length;
// there will definitely always be a ClinicalDocument root
let index = 0;
let node = cdaDocument.getXPathElements('/' + paths[index++])[0];
for (; index < len; index++) {
const path = paths[index];
const nodes = node.getXPathElements('/' + path);
if (!nodes.length) {
// node doesn't exist - add it
const newChild = cdaDocument.createNewElement(path);
node.appendChild(newChild);
node = newChild;
} else {
node = nodes[0];
}
}
return node;
}
/**
* Adds the <sdtc:ethnicGroupCode> node to the document,
* creating the patient hierarchy on the document if required.
*/
function addEthnicGroupCodeToPatientNode(coding, cdaDoc) {
// find the patient node on this document
const patientNode = getPatientNodeOnDoc(cdaDoc);
// add the xmlns:sdtc namespace
cdaDoc.addNamespace('xmlns:sdtc', 'urn:hl7-org:sdtc', 'http://www.w3.org/2000/xmlns/');
// create the element
const ethnicGroupCode = cdaDoc.createNewElement('sdtc:ethnicGroupCode');
ethnicGroupCode.setOrAddAttribute('code', coding.code);
ethnicGroupCode.setOrAddAttribute('displayName', coding.display);
ethnicGroupCode.setOrAddAttribute('codeSystem', coding.system.replaceAll('urn:oid:', ''));
ethnicGroupCode.setOrAddAttribute('codeSystemName', 'Race & Ethnicity - CDC');
// add it to the dom
patientNode.appendChild(ethnicGroupCode);
}