The EHR codebase extends the standard
LabKey Server trigger script framework to make it easier to do things like:
- Extend and customize the core EHR module's trigger script functionality
- Hook into both update and insert actions
- Only perform work when a record is moved to the Completed QC state, so that saving records in a draft or request status doesn't cause the code to fire
Topics:
Conventions
All study datasets and other tables that want to participate in the EHR trigger script codebase should have a trigger script. The script must initialize the EHR triggers. Many scripts will also want to import the EHR.Server.Utils object as well:
require("ehr/triggers").initScript(this);
EHR.Server.Utils = require("ehr/utils").EHR.Server.Utils;
Additional Functions
The EHR framework provides additional functions that a trigger script may implement which will be passed additional context such as a ScriptHelper argument, beyond the basic information that is provided to the standard trigger script functions like beforeUpdate and afterDelete.
- onInit - invoked once before any of the rows are processed
- onUpsert - invoked for each row before either an insert or update is performed. See information in the docs for EHR.Server.Triggers.beforeInsert in ehrModules/ehr/resources/scripts/ehr/triggers.js
- onInsert - invoked for each row before an insert is performed. See information in the docs for EHR.Server.Triggers.beforeInsert in ehrModules/ehr/resources/scripts/ehr/triggers.js
- onUpdate - invoked for each row before an insert is performed. See information in the docs for EHR.Server.Triggers.beforeUpdate in ehrModules/ehr/resources/scripts/ehr/triggers.js
- onDelete - invoked for each row before a delete is performed. See information in the docs for EHR.Server.Triggers.EHR.Server.Triggers.beforeDelete in ehrModules/ehr/resources/scripts/ehr/triggers.js
- onComplete - invoked once after all of the rows are processed
Handler Registration
The EHR framework also offers a mechanism to register function handlers for types of events that may occur during the processing of the rows. This allows finer-grained registration, can make it easier to reuse code across multiple tables, and for a center-specific module to augment the core EHR module's implementation.
See the various "registerHandler" functions in EHR.Server.TriggerManager in ehrModules/ehr/resources/scripts/ehr/triggerManager.js for more details.
The events supported, also described in the EHR.Server.TriggerManager code, include:
- INIT - equivalent to the standard trigger script function init()
- BEFORE_INSERT - equivalent to the standard trigger script function beforeInsert()
- AFTER_INSERT - equivalent to the standard trigger script function afterInsert()
- BEFORE_DELETE - equivalent to the standard trigger script function beforeDelete()
- AFTER_DELETE - equivalent to the standard trigger script function afterDelete()
- BEFORE_UPDATE - equivalent to the standard trigger script function beforeUpdate()
- AFTER_UPDATE - equivalent to the standard trigger script function afterDelete()
- BEFORE_UPSERT - invoked before both insert and update events on a per-row basis
- AFTER_UPSERT - invoked after both insert and update events on a per-row basis
- ON_BECOME_PUBLIC - invoked before a record is moving into the 'Completed' QC state, whether it is being inserted directly into that state, or if an existing draft or request record is being updated
- AFTER_BECOME_PUBLIC - invoked after a record is moving into the 'Completed' QC state, whether it is being inserted directly into that state, or if an existing draft or request record is being updated
- COMPLETE - equivalent to the standard trigger script function complete()
- DESCRIPTION - generates a single-string value, returned from the function supplied, that captures the important details from the full set of fields for the row
Any processing will be in addition to the other trigger script code implemented in other registered handlers, or in any onUpsert() or other functions.
Scripts can also detect when a row is "becoming public" by looking for a 'true' value for the '_becomingPublicData' property on the row object.
Module-Specific Extensions
Center-specific modules may provide a centralized script file that registers handlers for many different tables via the handler mechanism described above. For example, WNPRC's module includes a file resources/scripts/wnprc_ehr/wnprc_triggers.js. It is registered in WNPRC_EHRModule.java's doStartupAfterSpringConfig() method:
Resource r = getModuleResource("/scripts/wnprc_ehr/wnprc_triggers.js");
EHRService.get().registerTriggerScript(this, r);
ScriptHelper
The EHR-specific functions and handlers are passed a helper object, which is implemented by ehrModules/ehr/resources/scripts/ehr/ScriptHelper.js, and provides utility functionality and context about the script that is being executed. See that file for full details.
The helper can also be configured to customize the core EHR module's trigger script behavior. For example, the default validation of the core EHR triggers prevents users from entering data for unknown animal IDs. Since the birth and arrival datasets are used to establish a new animal within the colony, the need to signal that it's OK for them to receive an as-of-yet-unknown ID. They do this by configuring the script helper in their onInit() functions, simplified below:
function onInit(event, helper){
helper.setScriptOptions({
allowAnyId: true
});
}
TriggerScriptHelper
While the majority of the EHR trigger script code is implemented in JavaScript, there are some operations that are either easier or more performant to implement in Java. The class TriggerScriptHelper in ehrModules/ehr/src/org/labkey/ehr/utils/TriggerScriptHelper.java provides the entry point into this Java code.
An instance of the Java-based TriggerScriptHelper is available via the JavaScript ScriptHelper object. A somewhat contrived example usage looks like:
function onComplete(event, errors, helper) {
helper.getJavaHelper().closeHousingRecords(['animalId1', 'animalId2']);
}
Standard Validation
The EHR trigger scripts perform many validations that are common across tables. These include checking that dates are within an expected range of the current date, animal IDs conform to expected naming conventions, and similar work. See the implementation in ehrModules/ehr/resources/scripts/ehr/triggers.js for much of this centralized, cross-table code.
Errors and Warnings
Trigger scripts often provide validation and general feedback to users. Errors should be associated with the problematic field to help the user interface display it as close to the field needing attention as possible.
The addError() function is a convenient way to do this.
ERROR level messages prevent the transaction from being completed. WARN and INFO level messages provide feedback to the user but don't prevent the transaction from completing.
function onUpsert(helper, scriptErrors, row, oldRow) {
if (row.quantity < 0)
EHR.Server.Utils.addError(scriptErrors, 'quantity', 'Quantity cannot be negative', 'ERROR');
if (row.quantity > 1000)
EHR.Server.Utils.addError(scriptErrors, 'quantity', 'Excessive quantity requested', 'WARN');
}
Related Topics