Skip to main content

Write to script processing table (extended from DL) to execute stored gliderecord scripts

write gliderecord script to a table which can then be executed on a schedule using GlideScopedEvaluator()

-business rules to trigger the update action
-script include to store entries in a scheduled script table (extended from DL [dl_matcher] so as to be outside of license cost)

-scheduled job to iterate through the scheduled script table and execute rows ready to be processed

CODE:

new table extended from DL table (as per Create custom data lookups (servicenow.com)





Business rules - just examples

REQ/RITM: after insert BR on REQ table, better solution would be a timer on the RITM workflow with the code in there, but this is an alternate solution if the workflow couldn't be updated for whatever reason. The challenge was picking up how many RITMs present on the REQ within the insert action end to end, which proved difficult using business rules alone

(function executeRule(current, previous /*null when async*/ ) { //--REQ short description auto populated from linked RITM, ONLY if ONE RITM is assigned to the request var si = new scriptAction_runscripts(true, false); var sScript = si.pull_shortdescr_ritm_to_req(current); if (sScript != '') { si.createScheduledScriptEntry('busRule:REQ insert routines-AFTER', current.number); //gs.addInfoMessage(current.number + ' - update scheduled'); } })(current, previous);







INCIDENT:

(function executeRule(current, previous /*null when async*/ ) { var si = new scriptAction_runscripts(true, false); var sWorknote = ''; var sSourceScript = 'busrule:Update child incident parent add/remove'; if (gs.nil(current.parent_incident)) { var prevIncMsg = previous.parent_incident.number; sWorknote = 'Incident ' + prevIncMsg + ' removed as a child'; } else { var currIncMsg = current.parent_incident.number; sWorknote = 'Incident ' + currIncMsg + ' added as a child'; } //--schedule the update to happen within a few seconds to avoid the double update: var res = si.childIncident_addworknote_buildscript(current.number, sWorknote); si.createScheduledScriptEntry(sSourceScript, current.number); })(current, previous);











script includes - main function is createScheduledScriptEntry

//--typically, a function will be called to build up a script and place it in ready state, and the createScheduledScriptEntry function is then called to fire the scheduled job on demand see example business rule with sysid "" for how this 2-step works (1. populate the script string, 2. push it to the 'u_hmcts_scheduled_scripts' table and execute the scheduled job on demand)

var scriptAction_runscripts = Class.create(); scriptAction_runscripts.prototype = { initialize: function(triggerJob, skipBusinessRules) { this.debug = true; //this.debug=false; this.scriptToExecute = ''; this.triggerSchJob = triggerJob; this.skipBusinessRules = skipBusinessRules; this.scheduledJob = 'HMCTS run scheduled scripts in ready state'; }, createScheduledScriptEntry: function(calledFrom, relatedTask) { //--params: //-- calledFrom: e.g. 'business Rule:', //-- relatedTask: e.g. 'REQ5003686' //this.triggerSchJob = false; if (this.scriptToExecute != '') { var grSScr = new GlideRecord('u_hmcts_scheduled_scripts'); grSScr.newRecord(); grSScr.u_script_to_execute = this.scriptToExecute; grSScr.u_called_from = calledFrom; grSScr.u_related_task = relatedTask; if (this.skipBusinessRules) { grSScr.u_skip_business_rules = true; } grSScr.u_status = 'ready'; grSScr.insert(); } if (this.triggerSchJob) { //--trigger the schJob to execute the script: var rec = new GlideRecord('sysauto_script'); rec.get('name', this.scheduledJob); SncTriggerSynchronizer.executeNow(rec); } this.scriptToExecute = ''; }, pull_shortdescr_ritm_to_req: function(REQobj) {
//--see https://tools.hmcts.net/jira/browse/ITSM-10859 //--since it was impossible to reliably check number of RITMs within the REQ/RITM business rules, this approach was to schedule a script to push short description up to the REQ with a small delay //--as per ITSM-10859, short description maps from RITM to REQ only if number of RITMs=1 var s_script = 'var si=new scriptAction_runscripts("", "false");\r\n'; s_script = s_script + 'var icount=si.getAggregate_RITM("' + REQobj.sys_id + '");\r\n'; s_script = s_script + 'var grREQ=new GlideRecord("sc_request");\r\n'; s_script = s_script + 'if (grREQ.get("number", "' + REQobj.number + '")){;\r\n'; if (this.skipBusinessRules) { s_script = s_script + ' grREQ.autoSysFields(false);\r\n'; //--leave last updated intact s_script = s_script + ' grREQ.setWorkflow(false);\r\n'; //--skip business rules and notifications s_script = s_script + ' grREQ.setEngines(false);\r\n'; //--skip data policy rules } s_script = s_script + ' if (icount==1){;\r\n'; s_script = s_script + ' var grRITM=new GlideRecord("sc_req_item");\r\n'; s_script = s_script + ' grRITM.addQuery("request", "' + REQobj.sys_id + '");\r\n'; s_script = s_script + ' grRITM.query();\r\n'; s_script = s_script + ' if (grRITM.next()){\r\n'; s_script = s_script + ' grREQ.short_description=grRITM.short_description;\r\n'; s_script = s_script + ' grREQ.update();\r\n'; s_script = s_script + ' }\r\n'; s_script = s_script + ' }\r\n'; s_script = s_script + ' if (icount>1){;\r\n'; s_script = s_script + ' grREQ.short_description="Multiple Request Items";\r\n'; s_script = s_script + ' grREQ.update();\r\n'; s_script = s_script + ' }\r\n'; s_script = s_script + '}'; this.scriptToExecute = s_script; return (this.scriptToExecute); },
getAggregate_RITM: function(REQsysid) { //--as per best practice use GlideAggregate rather than getRowCount() //--https://developer.servicenow.com/dev.do#!/reference/api/orlando/server_legacy/c_GlideRecordAPI#r_GlideRecord-getRowCount var icount = 0; var agg = new GlideAggregate('sc_req_item'); agg.addQuery('request', REQsysid); agg.addAggregate("COUNT"); //agg.groupBy('short_description'); agg.query(); if (agg.next()) { icount = agg.getAggregate("COUNT"); } return icount; }, childIncident_addworknote_buildscript: function(incNumber, workNote, bSkipBusinessRules) { var s_script = 'var grINC=new GlideRecord("incident");\r\n'; s_script = s_script + 'grINC.addQuery("number","' + incNumber + '");\r\n'; s_script = s_script + 'grINC.query();\r\n'; s_script = s_script + 'if (grINC.next()){\r\n'; if (this.skipBusinessRules) { s_script = s_script + 'grINC.autoSysFields(false);\r\n'; //--leave last updated intact s_script = s_script + 'grINC.setWorkflow(false);\r\n'; //--skip business rules and notifications s_script = s_script + 'grINC.setEngines(false);\r\n'; //--skip data policy rules } s_script = s_script + 'grINC.work_notes="' + workNote + '";\r\n'; s_script = s_script + 'grINC.update();\r\n'; s_script = s_script + '}\r\n'; this.scriptToExecute = s_script; return (this.scriptToExecute); }, type: 'scriptAction_runscripts' };



























Scheduled job (on demand, called via script include)

do_sleep_schjob(1000); function do_sleep_schjob(milliseconds) { //--introduce a small delay var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) { processRowsInReadyState(); break; } } } function processRowsInReadyState() { var gr = new GlideRecord("u_scheduled_scripts"); gr.addActiveQuery(); gr.addQuery("u_status", "ready"); gr.orderBy("order"); gr.query(); gs.log('START JOB: ROW COUNT: ' + gr.getRowCount(), 'schjob:runScheduledScripts'); //--use arrays, as the gliderecord gets temperamental with glidescopedevaluator! var arrSYSIDS = []; while (gr.next()) { arrSYSIDS.push(gr.sys_id.toString());//--note, the toString is very important! } for (iC = 0; iC < arrSYSIDS.length; iC++) { eval_script(arrSYSIDS[iC]); } //--tidy up: var bTidyUp = false; //bTidyUp=true; if (bTidyUp) { tidyUpOldScripts(); } gs.log('END JOB: ROW COUNT: ' + gr.getRowCount(), 'schjob:attach'); function eval_script(sysid) { var gr = new GlideRecord("u_scheduled_scripts"); if (gr.get('sys_id', sysid)) { try { var obj_evaluator = new GlideScopedEvaluator(); var res = obj_evaluator.evaluateScript(gr, 'u_script_to_execute'); gr.u_execution_info = 'OK'; gr.u_status = 'processed'; gr.active = false; } catch (ex) { gr.u_execution_info = 'OK'; gr.u_status = 'error'; } var sRecState=gr.update(); //gs.print(res); //gs.print(sRecState); } } } function tidyUpOldScripts() { var delQuery = 'u_status=processed^sys_created_on<javascript:gs.beginningOfLastMonth()'; var grDel = new GlideRecord("u_scheduled_scripts"); grDel.addQuery(delQuery); grDel.deleteMultiple(); }

Comments

Popular posts from this blog

ServiceNow check for null or nil or empty (or not)

Haven't tested these all recently within global/local scopes, so feel free to have a play! option 1 use an encoded query embedded in the GlideRecord , e.g.  var grProf = new GlideRecord ( 'x_cls_clear_skye_i_profile' ); grProf . addQuery ( 'status=1^ owner=NULL ' ); grProf . query (); even better use the glideRecord  addNotNullQuery or addNullQuery option 2 JSUtil.nil / notNil (this might be the most powerful. See this link ) example: if ( current . operation () == 'insert' && JSUtil . notNil ( current . parent ) && ! current . work_effort . nil ())  option 3 there might be times when you need to get inside the GlideRecord and perform the check there, for example if the code goes down 2 optional routes depending on null / not null can use gs.nil : var grAppr = new GlideRecord ( 'sysapproval_approver' ); var grUser = new GlideRecord ( 'sys_user' ); if ( grUser . get ( 'sys_id' , current . approver )){...

Code a pause/wait - gs.sleep or gs.wait alternative, pause script for specified seconds (timer)

Code a pause/wait - gs.sleep / gs.wait alternative, pause script for specified seconds (timer)  e.g. 10 seconds: do_sleep ( 10000 ); function do_sleep ( milliseconds ) { var start = new Date (). getTime (); for ( var i = 0 ; i < 1e7 ; i ++) { if (( new Date (). getTime () - start ) > milliseconds ){ gs . print ( 'waking up!' ); break ; } } }