Server Scripting - fdlGitHub/ServiceNow GitHub Wiki
- Aggregate - addHaving function
- Dot-Walking or getRefRecord
- Large Query - Chunk result
- Evaluate a script
- Impersonate a user in a script
- Check if user is impersonating
- Tiny URL
- Date in any format
- Check catalog visibility
- Script Include : Multiple Inclusion
- Force an update on record(s)
- Checking date against schedule
- Copy variables from a record to another one
- Base 64 - Encode/Decode values
- Decrypt password
- URL - Use another field as sys_id
- URL - Open JSON view
- Force a Scheduled Job to run on a node
- Import XML file
- Get node info
- Get Java version
- Database Aliases
- Refresh Table Cache
DEPRECEATED
var gr = new GlideAggregate('<table_name>');
gr.groupBy('<field_name>');
gr.addHaving('COUNT', '>', 1);
gr.query();
//Use gr.addHaving('COUNT', '>', 0); and !gr.next() when searching to know if a record is not present
The dot-walking is deprecated. It was replaced by the getRefRecord() method.
To prevent performance issue (each dot-walking generate a SQL request) and to can validate an API before publish it into the Service Now Store, it's recommanded to use the getRefRecord method and to stock the result into a variable to be able to re-use it each time it's needed.
In the following example, current is a record of the Incident table
var callerGR = current.caller_id.getRefRecord();
gs.log('eMail address : ' + callerGR.email); // Instead of current.caller_id.email
gs.log('eMail address : ' + callerGR.vip); // Instead of current.caller_id.vip
ServiceNow Documentation : getRefRecord()
var gr = new GlideRecord(<TABLE_NAME>);
// gr.query(); // This statement should never be used here while using the chunker. It's the chunker itself that will do it
var chunkGR = chunker(gr, 100, false);
while(chunkGR()) {
while(gr.next()) {
// Do something
}
}
function chunker(gr, size, isDeleting) {
var window_start = 0;
var window_size = size;
var chunkyGR = gr;
return nextChunk;
function nextChunk() {
// Example: if window_start = 0 and window_size = 100, we need to remove 1 record to ensure that we are taking 100 tickets (0 to 99), else, we will take 101 tickets (0 to 100) and the 100 one will be take two times
gr.chooseWindow(window_start, window_start + window_size - 1);
gr.query();
var has_next = (window_start < gr.getRowCount());
// When we are doing a delete action, we don't need to increase the window_start
if(!isDeleting) {
window_start += window_size;
}
return has_next;
}
}
The method eval(<STRING_TO_BE_EVALUATED>) is deprecated. It was replaced by the GlideScopedEvaluator object.
ServiceNow Documentation : GlideScopedEvaluator
var answer = '';
var evaluator = new GlideScopedEvaluator();
var parms = {};
parms.key1 = 'value1';
var gr = new GlideRecord('content_page_rule');
gr.get('46bbe1390a0a0b170039d28d0e7b9973');
// advanced_condition = gs.getUser().hasRoles();
answer = evaluator.evaluateScript(gr, 'advanced_condition', parms);
gs.info(answer); // true
var currentUser = gs.getUserID();
try {
// Replace <USER_SYS_ID> with the sys_id of the user you would like to impersonate
gs.getSession().impersonate('<USER_SYS_ID>');
// Do something
}
catch(ex) {
gs.print(ex);
}
finally {
// Go back to the previous user
gs.getSession().impersonate(currentUser);
}
if (gs.getImpersonatingUserName() !== gs.getUserName() && gs.getImpersonatingUserName() !== null) {
// Impersonate in progress, gs.hasRole('impersonator') is returning false
}
To check visibility (user criteria) of categories and catalog items
GlideappCategory.get(CATEGORY_SYS_ID).canView()
GlideappCatalogItem.get(CAT_ITEM_SYS_ID).canView()
To be used with sysparm_tiny
var tinyUrl = new GlideTinyURL().createTinyURL(tableName + "_list.do?sysparm_query=sys_idIN" + sysIdList);
return tinyUrl;
Format available here: https://docs.oracle.com/javase/10/docs/api/java/text/SimpleDateFormat.html
new GlideDate().getByFormat('dd.MM.yyyy HH:mm:ss.SSS')
Declare your first Script Include (e.g : scriptLog)
var scriptLog= Class.create();
scriptLog.prototype = {
enableDebug: function(value) {
return value;
},
type: 'scriptLog'
};
Then declare a new one (e.g : scriptUtil)
var scriptUtil = Class.create();
scriptUtil.prototype = {
_isUtil: function() {
return false;
},
type: 'scriptUtil'
};
You are now able to declare the main Script Include with two first Script Include as extension
var mainScriptInclude = Class.create();
mainScriptInclude.prototype = Object.extendsObject(scriptLog, scriptUtil.prototype);
// mainScriptInclude have now methods of scriptLog and scriptUtil as local methods
// Extend again the mainScriptInclude by adding other methods
mainScriptInclude.prototype = Object.extendsObject(mainScriptInclude, {
initialize: function() {
gs.info('scriptLog method said : ' + this.enableDebug(true); // Result : true
gs.info('scriptUtil method said : ' + this._isUtil()); // Result : false
},
process: function() {
gs.info('Processing...');
},
type: 'mainScriptInclude'
});
It can be write as well like this:
var mainScriptInclude = Class.create();
mainScriptInclude.prototype = Object.extendsObject(scriptLog, {});
mainScriptInclude.prototype = Object.extendsObject(scriptUtil, mainScriptInclude.prototype);
// mainScriptInclude have now methods of scriptLog and scriptUtil as local methods
// Extend again the mainScriptInclude by adding other methods
mainScriptInclude.prototype = Object.extendsObject(mainScriptInclude, {
initialize: function() {
gs.info('scriptLog method said : ' + this.enableDebug(true); // Result : true
gs.info('scriptUtil method said : ' + this._isUtil()); // Result : false
},
process: function() {
gs.info('Processing...');
},
type: 'mainScriptInclude'
});
You can update records (run Business Rules) without updating a field. To do it, you need to use the setForceUpdate methog of the GlideRecord object.
Available only in the Global scope.
ServiceNow Documentation : setForceUpdate
// Replace <TABLE_NAME> by the name of a table (e.g : incident)
var tableGR = new GlideRecord('<TABLE_NAME>');
// Apply a query if necessary
tableGR.query();
while(tableGR.next()) {
tableGR.setForceUpdate(true);
tableGR.update();
}
ServiceNow Documentation : IsInSchedule
var gdt = new GlideDateTime('2017-11-20 15:00:00');
var scheduleGS = new GlideSchedule('<SYS_ID>');
var inSchedule = scheduleGS.isInSchedule(gdt);
After submitting the form, variables displayed into a record producer are stored into:
- Questions Answers [question_answer] table for Task [task] (and inherited) tables
- Variable Ownerships [sc_item_option_mtom] table for Catalog Services tables
var questionAnswerGR = new GlideRecord('question_answer');
questionAnswerGR.addQuery('table_name', <table_name>);
questionAnswerGR.addQuery('table_sys_id', <sys_id>);
questionAnswerGR.query();
while(questionAnswerGR.next()) {
questionAnswerGR.table_name = <new_table_name>;
questionAnswerGR.table_sys_id = <new_sys_id>;
questionAnswerGR.insert();
}
The combo Username and Password can be or need to be encrypted like username:password. To encode/decode this syntax, use the following script:
GlideStringUtil.base64Decode('<encoded_base64>')
GlideStringUtil.base64Encode('<username:password>')
To obtain the clear text value of Password (2 way encrypted) fields, use following script :
var encrypter = new GlideEncrypter();
encrypter.decrypt(<Encrypted value>);
ServiceNow Documentation : GlideEncrypterAPI
Replace <Field_Name> by the password field name whom you want have
gr.<Field_Name>.getDecryptedValue()
ServiceNow Documentation : GetDecryptedValue
By using the sysparm_refkey parameter into a URL, you can open the form of a record by using another key than the sys_id
https://instancename.service-now.com/sys_dictionary.do?sys_id=assignmentgroup&sysparm_refkey=element
By using the specified URL, you can open the JSON view
https://instancename.service-now.com/TABLE_NAME.do?sysparm_sys_id=SYS_ID&sysparm_target=u_response_body&sysparm_action=getRecordColumn&JSONv2=
Be careful: Once the job is finished, the system will create, based on the Scheduled Job Definition, a new "Today's Scheduled Job" (sys_trigger) and will keep it assigned to the node.
To make sure that this job will not get stuck at some point (node offline, clone not cleaning properly the job, ...), don't forget to clear the system_id
field.
var triggerGR = new GlideRecord('sys_trigger');
triggerGR.get('TRIGGER_SYS_ID');
triggerGR.system_id = 'SERVER_NAME_NODE_NAME';
triggerGR.update();
Be careful: Method not documented by ServiceNow, so not supported!
var streamLoader = new GlideStreamLoader();
streamLoader.loadXML('<xml><unload format></unload format>');
Be careful: Method not documented by ServiceNow, so not supported!
GlideProperties.get("glide.cluster.node_name") => name of the node
gs.getNodeID() => sys_id of the node
gs.getSystemID() => server:node name
Be careful: Method not documented by ServiceNow, so not supported!
gs.getJavaVersion()
Database aliases are stored into sys_storage_table_alias for tables.
Database aliases are stored into sys_storage_alias for fields.
Following script has to be executed as a background script on one node; it will automatically flush the cache of all other application nodes
GlideTableManager.invalidateTable('table_name');
GlideCacheManager.flushTable('table_name');
Documentation: KB0695145 - Table alters via update set sometimes do not invalidate cache correctly on other nodes
Be careful: Method not documented by ServiceNow, so not supported! NO LONGER USABLE SINCE LONDON
GlideDBUtil.promoteColumn('cmdb_ci', 'cmdb_ci_service', 'u_yourfield', false);
Documentation: Moving a column from a parent table to an extended table
ServiceNow Documentation: Move a field from a child to a parent table does not work with promoteColumn()
Be careful: Method not documented by ServiceNow, so not supported! NO LONGER USABLE SINCE LONDON
GlideDBUtil.promoteColumn('cmdb_ci_service', 'cmdb_ci', 'u_yourfield', true);
Documentation: Promote a Field from an Extended Table in ServiceNow
ServiceNow Documentation: Move a field from a child to a parent table does not work with promoteColumn()