Skip to content

Instantly share code, notes, and snippets.

@mhokanson
Last active November 6, 2025 01:48
Show Gist options
  • Select an option

  • Save mhokanson/1ba83e6913817358c043b96e205e3499 to your computer and use it in GitHub Desktop.

Select an option

Save mhokanson/1ba83e6913817358c043b96e205e3499 to your computer and use it in GitHub Desktop.
Nextgen/Mirth Connect testing in VSCode

Requires:

  • Mirth Connect to be installed locally
  • VSCode task set up with paths pointed to the local Mirth instance (provided file are for default Windows locations)
  • vs_mirth.js should be placed in the location specified in line 14 of the build task (arg 4)
  • Executed file has to be based on a saved file NOTE: Changes don't have to be saved to execute as the build task will save the file if needed.
var msgString = "MSH|^~\\&|system1|TEST|system2|UHN|200105231927||ADT^A01^ADT_A01|22139243|P|2.4\n" +
"EVN|A01|200105231927|\n" +
"PID||9999999999^^|2216506^||Duck^Donald^^^MR.^MR.||19720227|M|||123 Foo ST.^^TORONTO^ON^M6G 3E6^CA^H^~123 Foo ST.^^TORONTO^ON^M6G 3E6^CA^M^|1811|(416)111-1111||E^ ENGLISH|S| PATIENT DID NOT INDICATE|211004554^||||||||||||\n" +
"PV1|||ZFAST TRACK^WAITING^13|E^EMERGENCY||369^6^13^U EM EMERGENCY DEPARTMENT^ZFAST TRACK WAITING^FT WAIT 13^FTWAIT13^FT WAITING^FTWAIT13|^MOUSE^MICKEY^M^^DR.^MD|||SUR||||||||I|211004554^||||||||||||||||||||W|||||200105231927|||||\n" +
"PV2||F|^R/O APPENDICIAL ABSCESS|||||||||||||||||||||||||\n" +
"IN1|1||001001| OHIP||||||||||||^^^^^|||^^^^^^M^|||||||||||||||||||||||||^^^^^^M^|||||\n" +
"ACC|\n"
var msg = new XML(SerializerFactory.getSerializer('HL7V2').toXML(msgString))
logger.info(msg['MSH']['MSH.4']['MSH.4.1'].toString())
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Run in Rhino shell",
"type": "process",
"command": "java",
"args": [
"org.mozilla.javascript.tools.shell.Main",
"-version",
"200",
"${env:USERPROFILE}/AppData/Roaming/vs_mirth/vs_mirth.js",
"${file}"
],
// "options": {
// "env": {
// "CLASSPATH": "./*:./commons/*:./database/*:./donkey/*:./javax/*:./jersey/*:./jetty/*:./jms/*:./swagger/*:./javax/jaxb/*:./javax/jaxws/*:./javax/jaxb/ext/*:./javax/jaxws/ext/*:./jersey/ext/*:./jetty/jsp/*:./swagger/ext/*:./custom-lib/*:../extensions/dashboardstatus/*:../extensions/datapruner/*:../extensions/datatype-delimited/*:../extensions/datatype-dicom/*:../extensions/datatype-edi/*:../extensions/datatype-hl7v2/*:../extensions/datatype-hl7v3/*:../extensions/datatype-json/*:../extensions/datatype-ncpdp/*:../extensions/datatype-raw/*:../extensions/datatype-xml/*:../extensions/destinationsetfilter/*:../extensions/dicom/*:../extensions/dicomviewer/*:../extensions/directoryresource/*:../extensions/doc/*:../extensions/extensionmanager/*:../extensions/file/*:../extensions/globalmapviewer/*:../extensions/http/*:../extensions/httpauth/*:../extensions/imageviewer/*:../extensions/javascriptrule/*:../extensions/javascriptstep/*:../extensions/jdbc/*:../extensions/jms/*:../extensions/js/*:../extensions/mapper/*:../extensions/messagebuilder/*:../extensions/mllpmode/*:../extensions/pdfviewer/*:../extensions/rulebuilder/*:../extensions/scriptfilerule/*:../extensions/scriptfilestep/*:../extensions/serverlog/*:../extensions/smtp/*:../extensions/tcp/*:../extensions/textviewer/*:../extensions/vm/*:../extensions/ws/*:../extensions/xsltstep/*:../extensions/dicom/lib/*:../extensions/dicomviewer/lib/*:../extensions/doc/lib/*:../extensions/file/lib/*:../extensions/httpauth/src/*:../extensions/pdfviewer/lib/*:../extensions/ws/lib/*"
// },
// "cwd": "$HOME/mirth/release/3.7.1/server-lib"
// },
"windows": {
"options": {
"env": {
"CLASSPATH": ".\\*;.\\commons\\*;.\\database\\*;.\\donkey\\*;.\\javax\\*;.\\jersey\\*;.\\jetty\\*;.\\jms\\*;.\\swagger\\*;.\\javax\\jaxb\\*;.\\javax\\jaxws\\*;.\\javax\\jaxb\\ext\\*;.\\javax\\jaxws\\ext\\*;.\\jersey\\ext\\*;.\\jetty\\jsp\\*;.\\swagger\\ext\\*;.\\custom-lib\\*;..\\extensions\\dashboardstatus\\*;..\\extensions\\datapruner\\*;..\\extensions\\datatype-delimited\\*;..\\extensions\\datatype-dicom\\*;..\\extensions\\datatype-edi\\*;..\\extensions\\datatype-hl7v2\\*;..\\extensions\\datatype-hl7v3\\*;..\\extensions\\datatype-json\\*;..\\extensions\\datatype-ncpdp\\*;..\\extensions\\datatype-raw\\*;..\\extensions\\datatype-xml\\*;..\\extensions\\destinationsetfilter\\*;..\\extensions\\dicom\\*;..\\extensions\\dicomviewer\\*;..\\extensions\\directoryresource\\*;..\\extensions\\doc\\*;..\\extensions\\extensionmanager\\*;..\\extensions\\file\\*;..\\extensions\\globalmapviewer\\*;..\\extensions\\http\\*;..\\extensions\\httpauth\\*;..\\extensions\\imageviewer\\*;..\\extensions\\javascriptrule\\*;..\\extensions\\javascriptstep\\*;..\\extensions\\jdbc\\*;..\\extensions\\jms\\*;..\\extensions\\js\\*;..\\extensions\\mapper\\*;..\\extensions\\messagebuilder\\*;..\\extensions\\mllpmode\\*;..\\extensions\\pdfviewer\\*;..\\extensions\\rulebuilder\\*;..\\extensions\\scriptfilerule\\*;..\\extensions\\scriptfilestep\\*;..\\extensions\\serverlog\\*;..\\extensions\\smtp\\*;..\\extensions\\tcp\\*;..\\extensions\\textviewer\\*;..\\extensions\\vm\\*;..\\extensions\\ws\\*;..\\extensions\\xsltstep\\*;..\\extensions\\dicom\\lib\\*;..\\extensions\\dicomviewer\\lib\\*;..\\extensions\\doc\\lib\\*;..\\extensions\\file\\lib\\*;..\\extensions\\httpauth\\src\\*;..\\extensions\\pdfviewer\\lib\\*;..\\extensions\\ws\\lib\\*;..\\client-lib\\userutil-sources.jar;C:\\Program Files\\Mirth Connect\\server-lib\\hapi\\*;"
},
"cwd": "C:\\Program Files\\Mirth Connect\\server-lib"
}
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
importPackage(com.mirth.connect.server.userutil)
/*
* Some of the functions in this module are Copyright (c) Mirth Corporation and are
* published under the terms of the MPL.
*
* Original source available here:
* https://github.com/nextgenhealthcare/connect/blob/3.8.x/server/src/com/mirth/connect/server/builders/JavaScriptBuilder.java
*/
// Written by Tony Germano and found at https://forums.mirthproject.io/forum/mirth-connect/support/18382-run-javascript-in-channel-context-outside-of-mirth?p=101745#post101745
var SerializerFactory = (function () {
var datatypes = com.mirth.connect.plugins.datatypes;
var dataPluginClasses = [
datatypes.delimited.DelimitedDataTypeServerPlugin,
datatypes.dicom.DICOMDataTypeServerPlugin,
datatypes.edi.EDIDataTypeServerPlugin,
datatypes.hl7v2.HL7v2DataTypeServerPlugin,
datatypes.hl7v3.HL7V3DataTypeServerPlugin,
datatypes.json.JSONDataTypeServerPlugin,
datatypes.ncpdp.NCPDPDataTypeServerPlugin,
datatypes.xml.XMLDataTypeServerPlugin
];
var dataPlugins = new org.apache.commons.collections4.map.CaseInsensitiveMap(
dataPluginClasses.reduce(function (obj, javaClass) {
try {
var instance = new javaClass();
obj[instance.getPluginPointName()] = instance;
}
catch (e) { }
return obj;
}, {})
);
function addDataTypeServerPlugin(javaClass) {
try {
var instance = new javaClass();
dataPlugins.put(instance.getPluginPointName(), instance);
}
catch (e) { }
}
function getSerializer(dataType, serializationPropertiesMap, deserializationPropertiesMap) {
return (arguments.length === 1)
? _getSerializer(dataType, null, null)
: _getSerializer(dataType, serializationPropertiesMap, deserializationPropertiesMap);
function _getSerializer(dataType, serializationPropertiesMap, deserializationPropertiesMap) {
var plugin = dataPlugins.get(dataType);
if (plugin != null) {
if (serializationPropertiesMap == null) {
serializationPropertiesMap = getDefaultSerializationProperties(dataType);
}
if (deserializationPropertiesMap == null) {
deserializationPropertiesMap = getDefaultDeserializationProperties(dataType);
}
var properties = plugin.getDefaultProperties().getSerializerProperties();
if (properties.getSerializationProperties() != null) {
properties.getSerializationProperties().setProperties(serializationPropertiesMap);
}
if (properties.getDeserializationProperties() != null) {
properties.getDeserializationProperties().setProperties(deserializationPropertiesMap);
}
return plugin.getSerializer(properties);
} else {
return null;
}
}
}
function getDefaultSerializationProperties(dataType) {
var plugin = dataPlugins.get(dataType);
if (plugin != null && plugin.getDefaultProperties().getSerializationProperties() != null) {
return plugin.getDefaultProperties().getSerializationProperties().getProperties();
} else {
return null;
}
}
function getDefaultDeserializationProperties(dataType) {
var plugin = dataPlugins.get(dataType);
if (plugin != null && plugin.getDefaultProperties().getDeserializationProperties() != null) {
return plugin.getDefaultProperties().getDeserializationProperties().getProperties();
} else {
return null;
}
}
return {
getDefaultSerializationProperties: getDefaultSerializationProperties,
getDefaultDeserializationProperties: getDefaultDeserializationProperties,
getSerializer: getSerializer,
addDataTypeServerPlugin: addDataTypeServerPlugin
};
})();
function validate(mapping, defaultValue, replacement) {
var result = (mapping == undefined) ? undefined : mapping;
if ((result == undefined) || (result.toString().length == 0)) {
if (defaultValue == undefined) {
defaultValue = '';
}
result = defaultValue;
}
if ('string' === typeof result || result instanceof java.lang.String || 'xml' === typeof result) {
result = new java.lang.String(result.toString());
if (replacement != undefined) {
for (var i = 0; i < replacement.length; i++) {
var entry = replacement[i];
result = result.replaceAll(entry[0], entry[1]);
}
}
}
return result;
};
// Helper function to create segments
function createSegment(name, msgObj, index) {
if (arguments.length == 1) { return new XML('<' + name + '></' + name + '>'); };
if (arguments.length == 2) { index = 0; };
msgObj[name][index] = new XML('<' + name + '></' + name + '>');
return msgObj[name][index];
};
// Helper function to create segments after specified field
function createSegmentAfter(name, segment) {
var msgObj = segment;
while (msgObj.parent() != undefined) { msgObj = msgObj.parent(); }
msgObj.insertChildAfter(segment[0], new XML('<' + name + '></' + name + '>'));
return msgObj.child(segment[0].childIndex() + 1);
};
// Helper function to get the length of an XMLList or array
function getArrayOrXmlLength(obj) {
if (typeof obj == 'xml' || obj instanceof java.lang.String) {
return obj.length();
} else if (typeof obj != 'undefined' && obj != null) {
return obj.length || 0;
}
return 0;
};
// function $(variableName){
// if(variableName == 'isDebug') {
// return false
// }
// if(variableName == 'countIndex') {
// return null
// }
// }
var mapObj = {
"put": function(key,value){
this.storedValues[key] = value
},
"storedValues":{},
"get": function(key){
return this.storedValues[key];
},
"containsKey": function(key){
if(typeof this.storedValues[key] != "undefined"){
return true;
}
return false;
}
}
var connectorMap = mapObj
var channelMap = mapObj
var sourceMap = mapObj
var globalChannelMap = mapObj
var globalMap = mapObj
var configurationMap = mapObj
var responseMap = mapObj
function $co(key, value) { if (arguments.length == 1) { return connectorMap.get(key); } else { return connectorMap.put(key, value); } }
function $c(key, value) { if (arguments.length == 1) { return channelMap.get(key); } else { return channelMap.put(key, value); } }
function $s(key, value) { if (arguments.length == 1) { return sourceMap.get(key); } else { return sourceMap.put(key, value); } }
function $gc(key, value) { if (arguments.length == 1) { return globalChannelMap.get(key); } else { return globalChannelMap.put(key, value); } }
function $g(key, value) { if (arguments.length == 1) { return globalMap.get(key); } else { return globalMap.put(key, value); } }
function $cfg(key, value) { if (arguments.length == 1) { return configurationMap.get(key); } else { return configurationMap.put(key, value); } }
function $r(key, value) { if (arguments.length == 1) { return responseMap.get(key); } else { return responseMap.put(key, value); } }
function $(string) {
try { if(responseMap.containsKey(string)) { return $r(string); } } catch(e){}
try { if(connectorMap.containsKey(string)) { return $co(string); } } catch(e){}
try { if(channelMap.containsKey(string)) { return $c(string); } } catch(e){}
try { if(sourceMap.containsKey(string)) { return $s(string); } } catch(e){}
try { if(globalChannelMap.containsKey(string)) { return $gc(string); } } catch(e){}
try { if(globalMap.containsKey(string)) { return $g(string); } } catch(e){}
try { if(configurationMap.containsKey(string)) { return $cfg(string); } } catch(e){}
try { if(resultMap.containsKey(string)) { return resultMap.get(string); } } catch(e){}
return '';
}
var destinationSet = {
"destinations": new java.util.HashMap(),
"remove": function(destinationsToRemove) {
if(Object.prototype.toString.call(destinationsToRemove) != '[object Array]'){
logger.error("destinations to be removed must be in an array")
return false;
}
for(var i = 0; i < destinationsToRemove.length; i++){
this.destinations = this.destinations.splice(this.destinations.indexOf(destinationsToRemove[i]), 1);
}
return true;
},
"removeAllExcept": function(destinationsToPreserve) {
if(Object.prototype.toString.call(destinationsToPreserve) != '[object Array]'){
logger.error("destinations to be preserved must be in an array")
return false;
}
// map removal adapted from https://stackoverflow.com/a/1884916
for(var i = this.destinations.entrySet().iterator(); i.hasNext(); ) {
var entry = i.next();
if(!destinationsToPreserve.includes(String(entry.getKey()))) {
i.remove();
}
}
return true;
},
"removeAll": function() {
this.destinations = [];
return true;
}
}
// the next two lines are for getting the current date and time in the local
// timezone in ISO-8601 format without the 'T' separator
//
// Take from https://stackoverflow.com/a/28149561
var tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -1).replace("T", " ");
var logger = {
"debug":function(message){
print("\033[30m" + localISOTime + "\t\033[34m[debug]\t\033[0m"+message)
},
"error":function(message){
print("\033[30m" + localISOTime + "\t\033[31m[error]\t\033[0m"+message)
},
"info":function(message){
print("\033[30m" + localISOTime + "\t\033[32m[info]\t\033[0m"+message)
},
"warn":function(message){
print("\033[30m" + localISOTime + "\t\033[33m[warn]\t\033[0m"+message)
}
}
var connectorMessage = {
getMessageId: function(){
return 1234;
},
getDestinationIdMap: function(){
return destinationSet.destinations;
}
}
var serverName = java.net.InetAddress.getLocalHost().getHostName();
/**************************************
* Run the file provided
*
* This was worked out with ChatGPT by Mike Hokanson
* to allow for easier setup/testing via a build task
* in VS Code
**************************************/
if (arguments.length === 0) {
print("❌ No script file passed to main.js");
quit(1);
}
load(arguments[0]);
@gibson9583
Copy link

Class path loads need to be updated for newer versions of mirth connect. When running 4.3.0, I had to explicitly load mirth-server.jar first, and needed to add hapi's path because of the server-lib directory structure change.

@mhokanson
Copy link
Author

@gibson9583 which OS are you using? I'm using Win11 with Mirth Connect 4.5.0 and using this task without issue.

@mhokanson
Copy link
Author

@gibson9583 - I don't know if this is still of interest to you, but I just updated the gist to not require loading the mirth stuff written by agermano in each script, but instead load as part of the build task.

@gibson9583
Copy link

Hey @mhokanson - yeah a little while back I had mine updated in a similar fashion.

Also, for reference to the previous comments (sorry for not replying) - I have been running this on MacOS. So my task is a little bit different for it to work properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment