Skip navigation

Since I have been harping on Soap so much I thought I would take a breather and talk REST a bit.  With the release of Fuji ServiceNow has improved on REST.  What I thought I would do here is demonstrate what ServiceNow provides for prototyping REST queries, and then how you can also do this with a SOAP client tool.

 

Pre-requisites:

 

1. SoapUI installed on your local machine.  Actually any REST client would work, but I will be using SoapUI for the purposes of this article.

Download SoapUI Open Source | Downloads

 

2. Some familiarity with RESTful Web Services.  If you are new to REST you might want to go read up on it a bit.  The following article may help:

RESTful Web Services: A Tutorial | Dr Dobb's

 

3. Some familiarity with JSON.  If you are new to JSON this article may help:

JSON Tutorial

 

4. Create a Web Services Group and User.  See my previous Web Services Part 1 labs for help with this.

 

 

Lab 1.1: Modeling the REST Query With the REST API Explorer

 

1. The Initial Query - Get Everything in the Table

 

a. Navigate to System Web Services -> REST -> REST API Explorer.  The REST API Explorer form will be displayed.  The default form is the same as the "Retrieve records from a table (GET)" link.

 

b. Fill in the form with the folowing:

 

  i.   Request Format: JSON

  ii.  Response Format: JSON

  iii.  Authorization: Send as me

  iv.  tableName: Incident

  v.   Leave everything else as a default (blank).  The default Limit will be 10.

 

 

c. Scroll to the bottom of the form and click on the Send button.  The results of the query will be displayed below the Send button.

 

d. Under the Request section you will see the label: HTTP Method / URI.  Copy the URL into your favorite editor for future use.

 

https://dev00001.service-now.com/api/now/table/incident?sysparm_limit=10&sysparm_query=&sysparm_fields=

 

 

e. Scroll all the way down and you will see the actual data retrieved in the Results section.  Quite a bit of day is returned.  Let's see if we can pare that down a bit.

 

 

2. Adding an Encoded Query - Between Two Dates

 

If you have already done the labs in my previous Web Services Part 1 article you will remember our old friend the "between" query?  Here we will be using it again to constrain further the number of records being returned.

 

a. While you are still on the REST API Explorer form scroll back up above the Send button to see the various query fields.

 

b. Locate the sysparm_query field.  Fill in the value with the following:

 

sys_updated_on>=2015-10-20 00:00:00^sys_updated_on<2015-10-20 23:59:59

 

Note that it is not necessary to URL encode the greater and less than signs.  You may have to fiddle with the dates to get a return as your data may be somewhat different than mine.

 

 

c. Click the Send button, and then scroll to the very bottom when the results are re-displayed.  You should notice a significant reduction in the number of records returned.

 

d. Note that the URL has now changed to look something like this:

 

https://dev000001.service-now.com/api/now/table/incident?sysparm_limit=10&sysparm_query=sys_updated_on>=2015-10-20 00:00:00^sys_updated_on<2015-10-20 23:59:59&sysparm_fields=

 

 

3. Limit the Fields That Are Returned

 

You should have noticed that we are getting every field back from the table.  Well you can limit the fields being brought back by specifying those you really want in a comma-delimited list.

 

a. Leaving everything as-is scroll to the Query Parameters again, and locate the sysparm_fields field.

 

b. Fill in the value with: sys_id

 

 

c. Click the Send button again.  Scroll to the bottom of the form again and you will note that only the sys_ids for each record are returned.

 

 

 

That wraps our short tour of the REST API Explorer.  Now, let's go try it all out in SoapUI!

 

 

 

Lab 1.2: Implementing the REST Query With SoapUI

 

SoapUI like other REST clients has the functionality built in to do REST queries.  We will be using similar query progression like we just used with the REST API Explorer.  So let's get started.

 

1. Setting up the project, and running the first query

 

a. In my ServiceNow instance I had already created a group and user for Web Services to use when accessing my instance.  You will need to do something similar.  make sure you give your Group the following roles:

 

  soap - for soap calls

  itil - for calls into ITIL tables

  rest_service - for REST calls

 

b. Open SoapUI.

 

c. On the menu navigate to File -> New REST Project.  This will display the New REST Project form.

 

 

 

d. Fill in the form with the URL you copied into your editor in Lab 1.1 Step 1d.  Click the OK button.  This will create a new REST Project.

 

 

 

e. In the Tree View right-click on the Request 1 label.  This will display the context menu.  Click on Rename.  The Rename form will be displayed.

 

f. Fill in the new name: getIncidents, and click the OK button.

 

 

g. In the Tree View right-click on the REST Project 1 label.  This will display the context menu.  Click on Rename.  The Rename form will be displayed.

 

h. Fill in the new name: ServiceNowREST, and click the OK button.

 

i. Click on getIncidents in the Tree View.  The properties for getIncidents will be displayed. 

 

j. Enter a web service user and password.  I am reusing the one that I created in my Web Services Part 1 article.

 

 

k. Now click on the green "play" button in the upper left of the getIncidents form.  This will run our REST query against our ServiceNow instance.

 

 

i. Make sure to click on the JSON tab to see your results.  They should look something like this:

 

 

 

 

2. Adding an encoded query

 

a. Click on the "+" button in the getIncidents form to add a new query parameter.

 

b. Fill in the parameter with the following:

 

  name: sysparm_query

  value: sys_updated_on>=2015-10-20 00:00:00^sys_updated_on<2015-10-20 23:59:59

 

  NOTE: The value will be the same "between" query you used in the previous lab.

 

c. Click on the "play button to run our query.  Again you will notice a significant reduction in the number of records.

 

 

 

 

3. Adding a fields limitation

 

a. Click on the "+" button in the getIncidents form to add another new query parameter.

 

b. Fill in the parameter with the following:

 

  name: sysparm_fields

  value: sys_id

 

c. Click on the "play button to run our query.  This time you will see that only the sys_id's will be returned.

 

 

And that is all there is to it!  If you have problems getting a return make sure to note the errors in the "SoapUI log" button at the bottom of the screen.  This will display what went wrong (for the most part).  Usually this will be a credentials or roles issue.

 

 

References:

 

REST API Wiki

Getting Started with REST Wiki

REST API Explorer Wiki

 

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

Also, if you are not already, I would like to encourage you to become a member of our blog!

Two things recently happened that prompted me to post this blog:

  1. I recently read Gregor Hohpe's article "Programming Without a Call Stack - Event-driven Architectures" found here: http://www.enterpriseintegrationpatterns.com/docs/EDA.pdf
  2. I was recently challenged to show how ServiceNow could support EDA within the context of scoped application development.

 

As with all things ServiceNow - the Platform is powerful and flexible enough to allow me to implement an EDA design pattern.  Within the scoped application development world of ServiceNow remember that application scoping ensures that one application does not impact another application.  They are mutually exclusive.  This kind of isolation between scoped applications makes it hard to take advantage of data and resources in the single data model - so maybe this is where an EDA comes in handy.

 

Remember the key characteristics (from the Gregor article) for an EDA are:

  • Broadcast communications
  • Timeliness
  • Asynchrony
  • Fine Grained
  • Ontology
  • Complex Event Processing

 

I think we have those requirements met by using the ServiceNow Event Queue.

 

Scoped applications have access to the Event Registry: Event Registry - ServiceNow Wiki

to fire events using gs.eventQueue and gs.eventQueueScheduled API calls.  They will only be able to select the tables (data model) that is in their scope.  For all other applications, outside of the application firing,  will need to create a Script Action Script Actions - ServiceNow Wiki  to handle custom events fired by application outside of their scope.

 

This is one way to implement inter-application communication using the EDA pattern.  Hope this helps.  Cheers.

 

AgendaTracker.jpg

 

AgTracker.jpg

In part four I reveal my madness!  You probably were saying to yourself: where is Bell going with all of this?  What could possibly be his motive for beating us to death with SoapUI and Web Services stuff?  Has he gone off the rails?

 

Um, well probably, on that last bit, but there really was a method to what I was about!

 

One of the things I get asked for, quite-a-bit:  Is it possible to add new functionality to the Help-the-Help-Desk (HtHD) script.  Short answer: No.  Everything is hard-coded by ServiceNow, and though you can modify the script the software on your instance will not recognize the new elements that have been added to the inbound XML.  I’ve tried it.  It does not work. 

 

So, noodling on this for awhile it dawned on me that there was actually a way to utilize the HtHD script and change the short answer to Yes!  Instead of going the normal route through the ECC Queue, why don’t we use a Scripted Web Service that would receive all of our new values and write them to the same record?  This, of course, would have to be done after the HtHD normal processing had ended. The ECC Queue is slower than a web service, and that has to be taken into account.

 

This would require that we add to the HtHD script.  No biggy as it is simply a JavaScript file.  So using the same methodology that we used when messing with SoapUI we create our own XML and send-to-web-service method, and tack it all onto the end of the HtHD JavaScript file.

 

Caveat:

 

1) This can currently only be done via the Microsoft Windows operating system.

 

Requirements:

 

1) For this you will need to have completed part 1 and part 3 of this article series.  You will need to know how to create and test a Scripted Web Service.

 

2) We will be modifying the Scripted Web Service you created in part 3. 

 

a. The modified script will need to be able to use the existing OOtB HtHD Script Include to test if the CI record exists to be updated. This SI is documented here Section 2.1.

b. It will need to wait and retry three times before erroring out.

 

3) Part 3 also adds the new fields to the Computer table that we will be populating from our HtHD modified script.

 

4) Download and modify the OOtB HtHD script to use WMI to collect our new values and then call into our Scripted Web Service.

 

5) To test we will be running the HtHD script from the Windows command line (cmd.exe).

 

 

Lab 1.1: Modifying the Scripted Web Service

 

We will be modifying the Scripted Web Service that we created in the last article to utilize the CIIdentifierForHelpDesk script include, and then do a series of retries.  The number of retries will be configurable by utilizing a new System Property.

 

1. Create a new System Properties:  Wait Time, Number of Retries.

 

a. Log onto your instance.

 

b. Navigate to sys_properties.list.  The System Properties list view will be displayed.

 

 

c. Click the New button.  The new system property form will be displayed.

 

d. Fill in the form with the following:

i. Name: sws.hthd.number_of_retries

ii. Description: Number of times the HtHD Scripted Web Service will retry an update to a given Configuration Item.

iii. Type: Integer

iv. Value: 3

v. Click Submit to Save your Work. The System Properties list view will be redisplayed.

 

 

e. Click the New button.  The new system property form will be displayed.

 

f. Fill in the form with the following:

i. Name: sws.hthd.wait_time

ii. Description: Amount of time in milliseconds to wait between retries

iii. Type: Integer

iv. Value: 5000

 

 

2. Modify the Scripted Web Service:

  • Use CIIdentiferForHelpDesk.matchOnSerial(...) to locate the appropriate CI to update.  This is the same method used by HtHD so we should be pretty safe using it to do what we want.  We will be putting this in a retry loop to account for a new CI being placed in CMDB by HtHD (at a retry of 5 seconds wait each that would be a total of a 15 second wait which should be plenty of time).
  • Use new System Properties, and handle the wait and retry.  Make sure that defaults exist so there are no infinite loops!
  • Use the message stacking technique to bring all of the gs.logs together.
  • Use try/catch to make sure nothing horrible happens.

 

a. Navigate to System Web Services -> Scripted Web Services.  The Scripted Web Services list view will be displayed.

b. Locate and edit it up the scripted web service UpdateComputerCI we created in the Part 3 article.

c. Replace the Script with the following:

 

var message = '';  
  
try {  
       message += '--->soapRequestXML: ' + soapRequestXML + '\n';  
         
       xmldoc = new XMLDocument(soapRequestXML);  
       var serialNumber = xmldoc.getNodeText("//serial_number") + '';  
         
       var sys_id = '';  
       var i = 0;  // retry count  
       // go get our system properties
       var numberOfRetries = gs.getProperty('sws.hthd.number_of_retries', 1);  
       var waitTime = gs.getProperty('sws.hthd.wait_time', 1000);  
       
       // start the loop, break if we find a CI
       while (i < numberOfRetries) {  
              // use HtHD matchOnSerial to locate our CI by serial number
              sys_id = new CIIdentifierForHelpDesk().matchOnSerial(serialNumber);  
              i += 1;  
              if (JSUtil.nil(sys_id)) {  
                     message += '---> having to retry.  retry: ' + i + '\n';  
                     // not found, sleep and try again  
                     Packages.java.lang.Thread.sleep(waitTime);  
              }  
              else {  
                     // we found it so we can stop  
                     break;  
              }  
       }  
       
       // We didn't find the CI, throw an error
       if (JSUtil.nil(sys_id)) {  
              throw 'Serial Number: ' + serialNumber + ' not found! Cannot update.';  
       }  
         
       message += '---> sys_id: ' + sys_id + '\n';  
       
       // We found the CI, now update it
       var cpuSocket = xmldoc.getNodeText("//cpu_socket") + '';  
       var cpuArchitecture = xmldoc.getNodeText("//cpu_architecture") + '';  
       var cpuProcessorType = xmldoc.getNodeText("//cpu_processor_type") + '';  
       var cpuFamily = xmldoc.getNodeText("//cpu_family") + '';  
       var cpuStatus = xmldoc.getNodeText("//cpu_status") + '';  
       
       var computerRecords = new GlideRecord('cmdb_ci_computer');  
       if (computerRecords.get(sys_id)) {  
              computerRecords.u_cpu_socket = cpuSocket;  
              computerRecords.u_cpu_architecture = cpuArchitecture;  
              computerRecords.u_cpu_processor_type = cpuProcessorType;  
              computerRecords.u_cpu_family = cpuFamily;  
              computerRecords.u_cpu_status = cpuStatus;  
              computerRecords.update();  
       }  
}  
catch (err) {  
       message += '---> ERROR: ' + err + '\n';  
}  
  
gs.log(message, 'SWS: UpdateComputerCI');  

 

d. Your Scripted Web Service should now look something like this:

 

 

e. Click the Update button to save your work.

 

Ok, that part is done.  Now on to modifying the HtHD Script!

 

 

Lab 1.2: Modifying the Help-the-Help-Desk.js File

 

We will be modifying the HtHD script to include all five of the new CPU data fields we added to the cmdb_ci_computer table.

 

1. Download the file from your instance to your local account. Location is not really important as long as you know where it is to modify and run it.

 

 

2. Rename the downloaded file from helpthehelpdesk.jsdbx to helpthehelpdesk.js.

 

3. Edit the file in your favorite JavaScript editor (I used Notepad++).

 

4. Change the “server” variable to be your local instance.

 

var server = "https://dev00001.service-now.com/";

 

5. Change the “httpUsername” variable to be your “svcuser” user name.

 

var httpUsername = "svcuser";

 

6. Encrypt the password and change the “httpPassword” variable to be your encrypted password value.

 

a. In your ServiceNow instance navigate to System Definition -> Help the Help Desk.  This will display the Properties for HtHD.  We will be using the username and password that we created in the Part 1 article. 

 

b. Click on the Save button.  This will automatically encrypt your password.

 

c. Copy the password and place it to one side for use in the next step.

 

 

d. Modify the httpPassword line by pasting your encrypted password here.  It should look something like this:

 

var httpPassword = "encrypt:ZZ4moXYz123aBcD=";

 

6. Change the “DEBUG” value to “true”.

 

var DEBUG = true;

 

7. Change the g_routines variable to include a call to our new method.

 

var g_routines = [

[ "OS Check", "win9X()", false ],

[ "Getting OS Configuration", "getMachine()", true ],

[ "Gathering Disk Configuration", "getLogicalDisks()", true ],

[ "Gathering Network Configuration", "getNetworkAdapters()", true ],

[ "Gathering Printer Configuration", "getPrinters()", true ],

[ "Connecting to registry", "connectRegistry()", true ],

[ "Gathering Software Licenses", "getSoftwareLicenses()", true ],

[ "Gathering Microsoft Licenses", " getMicrosoftLicenses()", true ],

[ "Gathering Software Configuration", "getSoftware()", true ],

[ "Communicating", "eccEvent()", true ],

[ "Getting Super Special Information", "UpdateComputerCI()", true ]

];

 

8. Add the following code to the end of the script:

 

/*  
1. Use Win32 to retrieve local CPU values 
2. Call a Scripted Web Service to store those values in CMDB for this box 
*/  
function UpdateComputerCI() {  
    var serialNumber = '';  
    var cpuSocket = '';  
    var cpuArchitecture = '';  
    var cpuProcessorType = '';  
    var cpuFamily = '';  
    var cpuStatus = '';  
    
    // Initialize our WMI query
    var items = gCIMV2Service.ExecQuery("Select * from Win32_ComputerSystemProduct");  
    var enumItems = new Enumerator(items);  
    for (; !enumItems.atEnd(); enumItems.moveNext()) {  
        var item = enumItems.item();  
        serialNumber = item.IdentifyingNumber;  
    }  
   
    // Query for Processor information - specifically for CPU info
    var items = gCIMV2Service.ExecQuery("Select * from Win32_Processor");  
    var enumItems = new Enumerator(items);  
    for (; !enumItems.atEnd(); enumItems.moveNext()) {  
        var specialItem = enumItems.item();  
        debug('---> SocketDesignation: ' + specialItem.SocketDesignation);  
        cpuSocket = specialItem.SocketDesignation;  
        debug('---> Status: ' + specialItem.Status);  
        cpuStatus = specialItem.Status;  
        debug('---> Family: ' + getFamily(specialItem.Family));  
        cpuFamily = getFamily(specialItem.Family);  
        debug('---> ProcessorType: ' + getProcessorType(specialItem.ProcessorType));  
        cpuProcessorType = getProcessorType(specialItem.ProcessorType);  
        debug('---> Architecture: ' + getArchitecture(specialItem.Architecture));  
        cpuArchitecture = getArchitecture(specialItem.Architecture);  
    }  
    
    // Initialize our SOAP XML
    xmlSOAP = new ActiveXObject("Microsoft.XMLDOM");  
    var xmlTitle = xmlSOAP.createProcessingInstruction("xml", "version=\"1.0\"")  
    xmlSOAP.appendChild(xmlTitle);  
  
    // Set up our SOAP Envelope
    var soapEnvelope = xmlSOAP.createElement("soap:Envelope");  
    soapEnvelope.setAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/");  
    soapEnvelope.setAttribute("xmlns:upd", "http://www.service-now.com/UpdateComputerCI");  
    xmlSOAP.appendChild(soapEnvelope);    
  
    // Create the Body tags
    var soapBody = xmlSOAP.createElement("soap:Body");  
    soapEnvelope.appendChild(soapBody);  
  
    // Create the Payload tags
    var payload = xmlSOAP.createElement("payload");  
    soapBody.appendChild(payload);  
  
    // Add our fields to the payload XML
    addField(payload, "serial_number", serialNumber);  
    addField(payload, "cpu_socket", cpuSocket);  
    addField(payload, "cpu_architecture", cpuArchitecture);  
    addField(payload, "cpu_processor_type", cpuProcessorType);  
    addField(payload, "cpu_family", cpuFamily);  
    addField(payload, "cpu_status", cpuStatus);  
          
    // Send the SOAP XML to the Scripted Web Service
    sendRequest(server + "UpdateComputerCI.do?SOAP", null, xmlSOAP);  
}    
  
// Return english value of numbered architecture field  
// Derived from values found at: https://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx  
function getArchitecture(archIndex) {  
       var archType = ["x86", "MIPS", "Alpha", "3", "PowerPC", "ARM", "ia64", "7", "8", "x64"];  
       return archType[archIndex];  
}  
  
// Return english value of numbered family field. This is a 1-based value so will be offset by one.  
// Derived from values found at: https://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx  
function getFamily(familyIndex) {  
       var familyType = [  
       "Other","Unknown","8086","80286","Intel386 Processor","Intel486 Processor",  
       "8087","80287","80387","80487","Pentium Brand","Pentium Pro","Pentium II",  
       "Pentium Processor with MMX Technology","Celeron","Pentium II Xeon","Pentium III",  
       "M1 Family","M2 Family",  
       "20","21","22","23","AMD Duron Processor Family","K5 Family","K6 Family",  
       "K6-2","K6-3","AMD Athlon Processor Family","AMD2900 Family","K6-2+","Power PC Family",  
       "Power PC 601","Power PC 603","Power PC 603+","Power PC 604","Power PC 620","Power PC X704",  
       "Power PC 750",  
       "40","41","42","43","44","45","46","47","Alpha Family","Alpha 21064",  
       "Alpha 21066","Alpha 21164","Alpha 21164PC","Alpha 21164a","Alpha 21264","Alpha 21364","56","57","58","59",  
       "60","61","62","63","MIPS Family","MIPS R4000","MIPS R4200","MIPS R4400","MIPS R4600","MIPS R10000",  
       "70","71","72","73","74","75","76","77","78","79",  
       "SPARC Family","SuperSPARC","microSPARC II","microSPARC IIep","UltraSPARC",  
       "UltraSPARC II","UltraSPARC IIi","UltraSPARC III","UltraSPARC IIIi","89",  
       "90","91","92","93","94","95","68040","68xxx Family","68000","68010",  
       "68020","68030","102","103","104","105","106","107","108","109",  
       "110","111","Hobbit Family","113","114","115","116","117","118","119",  
       "Crusoe TM5000 Family","Crusoe TM3000 Family","Efficeon TM8000 Family",  
       "123","124","125","126","127","Weitek","129",  
       "Itanium Processor","AMD Athlon 64 Processor Family","AMD Opteron Processor Family",  
       "133","134","135","136","137","138","139","140","141","142","143",  
       "PA-RISC Family","PA-RISC 8500","PA-RISC 8000","PA-RISC 7300LC","PA-RISC 7200",  
       "PA-RISC 7100LC","PA-RISC 7100",  
       "151","152","153","154","155","156","157","158","159",  
       "V30 Family","161","162","163","164","165","166","167","168","169",  
       "170","171","172","173","174","175","Pentium III Xeon Processor",  
       "Pentium III Processor with Intel SpeedStep Technology","Pentium 4","Intel Xeon",  
       "AS400 Family","Intel Xeon Processor MP","AMD Athlon XP Family","AMD Athlon MP Family",  
       "Intel Itanium 2","Intel Pentium M Processor",  
       "186","187","188","189","K7",  
       "191","192","193","194","195","196","197","Intel Core i7-2760QM","199",  
       "IBM390 Family","G4","G5","G6","z/Architecture Base","205","206","207","208","209",  
       "210","211","212","213","214","215","216","217","218","219",  
       "220","221","222","223","224","225","226","227","228","229",  
       "230","231","232","233","234","235","236","237","238","239",  
       "240","241","242","243","244","245","246","247","248","249",  
       "i860","i960","252","253","254","255","256","257","258","259",  
       "SH-3","SH-4","262","263","264","265","266","267","268","269",  
       "270","271","272","273","274","275","276","277","278","279",  
       "ARM","StrongARM","282","283","284","285","286","287","288","289",  
       "290","291","292","293","294","295","296","297","298","299",  
       "6x86","MediaGX","MII","303","304","305","306","307","308","309",  
       "310","311","312","313","314","315","316","317","318","319",  
       "WinChip","321","322","323","324","325","326","327","328","329",  
       "330","331","332","333","334","335","336","337","338","339",  
       "340","341","342","343","344","345","346","347","348","349",  
       "DSP","351","352","353","354","355","356","357","358","359"];  
       return familyType[familyIndex - 1];  
}  
  
// Return english value of Processor Type field. This is a 1-based value so will be offset by one.  
// Derived from values found at: https://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx  
function getProcessorType(procTypeIndex) {  
       var procType = ["Other", "Unknown", "Central Processor", "Math Processor", "DSP Processor", "Video Processor"];  
       return procType[procTypeIndex - 1];  
}  
  
// Return english value of Status Info field. This is a 1-based value so will be offset by one.  
// Derived from values found at: https://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx  
function getStatusInfo(statusIndex) {  
       var statusInfoType = ["Other", "Unknown", "Enabled", "Disabled", "Not Applicable"];  
       return statusInfoType[statusIndex - 1];  
} 

 

NOTE: 

I derived this code from initPayload, getMachine, etc.  The idea was to not mess with the existing OOtB script too much, but to rather tack the new code to the end; as much as possible.  I could have rewritten the whole thing, but instead took a minimalist approach.  In the process I stripped out a lot of the checking that probably should have been done, but I needed to leave you something to do!  Right?! 

 

 

Also, I threw in a quick-and-dirty way of translating the resultant coded values from Microsoft into something more useful in ServiceNow.

 

9. Save.

 

 

Lab 1.3: Testing

 

1. In Microsoft Windows navigate to Start and type in "cmd.exe" in the Search Commands and Files field.  Then click on cmd.exe to open a command window.  I always open this by right-clicking it to bring up the context menu and choosing "Run as Administrator" just to be safe.

 

 

2. Change directories (CD) to where the modified helpthehelpdesk.js now lives (you will probably want to rename this for production purposes).

 

3. Run the script using CScript.  Observe the results.

 

a. From the command line type the following and hit enter:  cscript helpthehelpdesk.js

 

 

We are interested with what shows last.  Our five new fields should be displayed at the end of the run with their respective values:

 

 

4. Checking the HtHD Status: Status

 

a. Navigate to System Definition -> Help the Help Desk Status.  The HtHD status list view will be displayed.

 

 

b. Order by Created date descending.

 

c. Click on the link of the most recent job that was run.  The HtHD Status form will be displayed.  Since we tested against our own device (laptop, desktop, or whatever) there should be a CI listed with a status of Created CI or Updated CI.

 

 

NOTE: If you are seeing this form everything worked as expected.  If not see Running HtHD, and follow the instructions to get it working right.

 

 

5. Go to the ECC queue.  We will just be verifying further that the normal HtHD script functioned.

 

a. Navigate to ECC -> Queue.  The ECC Queue list view will be displayed.

 

b. Order by Created date descending.

 

c. Filter for Topic:  WMILoader

 

d. You should see the most recent run of HtHD.  This will have a Name: of wmi.script, and a Source of your boxes name.

 

 

6. Now to check on whether our Scripted Web Service worked.  Navigate to the System Logs -> System Log -> All, and filter on Message: --->soap.  This should display the logged messages, and if everything worked should look like this:

 

 

7. Go to the CI and view the results.

 

a. Navigate to Configuration -> Base Items -> Computers.  This will display a list view of all Computer CIs.

 

b. Order by Updated date descending.  Your device should be the first record.

 

c. Edit it up your device by clicking on the link.  The Computer CI form should be displayed.

 

d. Note the values placed in our new fields by the HtHD script.  Your form should look something like this:

 

 

NOTE:  You might want to clear the fields before testing.

 

Now let's test our retry code!

 

8. New CI Creation Test

 

a. Go to Configuration -> Base Items -> Computers.  The Computers list view will be displayed.

 

b. Edit up your computer's CI, and click the Delete button.  The Delete Warning form will be displayed.  Click the Delete button to delete your CI.  This will reset everything for our test.

 

 

c. Now from the cmd.exe window re-run our test:  cscript helpthehelpdesk.js

 

d. Navigate to the System Logs -> System Log -> All, and filter on Message: --->soap.  This should display the logged messages.  You should now see a couple of retries in the log:

 

 

NOTE: If you were paying close attention you should have noticed that the HtHD script took a bit longer to run.  That is because there is a synchronous connection with the Web Service, and the cscript is waiting on the Scripted Web Service to complete before completing itself.  I believe this is a reason that the original HtHD developers chose to go with the ECC Queue which is Asynch.  So there is a bit of a trade-off here.  You could go with REST.  Just saying.

 

 

There you go!  You now have a way to add additional values to the HtHD Script.

 

More references you might want to read up on:

 

Win32_Processor

HtHD Login Script

Running HtHD

Data Collected by OOtB HtHD

 

I placed all of the files for this article out on the Share here.

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

Also, if you are not already, I would like to encourage you to become a member of our blog!

 

CS_logo.jpgMVP-logo.jpeg

Please Share, Like, Comment this blog if you've found it helpful or insightful.

 


For other Expert Knowledge

Ask A Developer       What’s In Store For IT Leaders in 2016       ServiceNow Online Learning Courses      ServiceNow Foundations: User Interface


 

The Content Management System (CMS) is a ServiceNow application that enables your company to create a custom interface of ServiceNow platform and applications. Active by default, CMS allows systems administrators or web developers to dive into projects ranging from customizing login pages to designing a complete website. In addition, non-technical users can take advantage of the application as an easy-to-use website maintenance tool. See CMS Planning for more information.

 

When using CMS, you may have encountered URLs or popups causing a blank window or page to appear on your screen. This can happen when you are attempt certain UI actions, such as:

  • Using the shift+click popup of a form
  • Launching a CMS page with an iFrame containing the URL of an external site
  • Using the Show Workflow UI action

 

If you are having these issues, here a some quick tips to fix your CMS pages:

 

Fix shift+click popup form and blank window

In CMS, when in the shift+click popup form, clicking a UI action causes the popup to become a blank window and remain open. For example, when you launch CMS and go to Service Catalog to review Open Orders, you can do a shift+click on the RITM number to open the popup form. The problem is, when you enter text into a field and update the form, the popup window goes white and remains open.

Requested_item_blank_window.JPG

 

You'll see that if you do this in the standard UI, the issues does not occur and the popup closes as expected. Simply click the "X" to close out the popup. Your update will still be saved as expected.

 

Correct iFrame with external website URL causing blank CMS page

When launching a CMS page with an iFrame that contains the URL of an external site,  you'll get a blank page as well as a console error message. For example, you may have a page containing an iFrame that specifies the URL of an external website:

 

External_link_iFrames_form.JPG

 

When the CMS page is launched, a blank page will appear. In the Chrome Developer Tools Console, you'll also get an error message: Refused to display '<url specified within IFrame>' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'

Error_console.JPG

 

This is caused by a security feature that blocks mixed content in browsers. In this case, displaying this type of content within an iFrame is not supported by the target domain/server (https://cloud.oracle.com in the above example) and browser security. Do not use an iFrame to add external links to the CMS page.

 

Here's an example where a link is added to the Get Help section on the ESS homepage that successfully opens in a new page:

Test_link.JPG

 

Fix Show Workflow UI action that generates wrong URL link

When you use the Show Workflow UI action in CMS, you'll end up with a blank page instead of the workflow. This is because the Show Workflow UI action is actually generating the wrong URL link with the CMS site prefix. For example, if you are in a change request associated with a workflow in ESS (https://<Eureka instance>.service-now.com/ess/change_request_list.do?sysparm_query=active%3Dtrue), and go to Related Links > Show Workflow, clicking the URL displays a blank page instead of the workflow:

Workflow_empty.jpg

 

 

Solution:

  1. After clicking the Show Workflow link, remove the part of the URL that references the CMS site name. For example: /ess
  2. Reload the page to show the correct workflow.
    workflow.jpg

Have more questions related to CMS?

ServiceNow KB: CMS Resources (KB0552847)

12 Questions to ask yourself when Troubleshooting CMS

Use a style property to correct overlapping scrollbars on an iFrame in IE11

Associate CMS to Service Catalog and UI buttons

Discreetly positioned on the email notification form is the ability to subscribe, marked "subscribable." The property which has been both cleverly and imaginatively designed, adds a subscribe to a record mechanism. On one side, someone can interpret it as the 'mandatory' option and others like the option so users can subscribe to the notifications.  I can say now, neither fish nor fowl. Two words very similar: subscribable and subscription. Have I confused you yet?

 

Subscription-icon-500x500.png

 

Let's talk about

  • Notification 'Subscribable' , 'Mandatory' and 'Force delivery' options
  • Subscribable option set to false
  • Subscribable option set to true
  • Testing the record-subscription based part when Subscribable is set to true

 

Subscribable, Mandatory and Force delivery options

On the email notification, you can set Subscribable, Mandatory and Force delivery to true or false to modify how recipients are included or excluded. These options define how the recipient list is generated from the notification user and group fields or from the cmn_notif_message table.

 

Here is the three email notification options in a nutshell:

Option

Value

Meaning

Purpose

Area of confusion

Subscribable

FALSE

Subscription based notifications

Normal notification

The value FALSE may look like the notification cannot be set on the notification preferences. Also the word subscribable make it looks like the mandatory field

Subscribable

TRUE

Subscribable based notifications

Add extra recipients to normal notifications based on records subscribed to by the users set on cmn_notif_message

Mandatory

TRUE

User Notification preference is mandatory to any user once they are valid recipients of the notification

User notification preference can be turn on/off but it is saved as ON whatever choice is selected

Field is hidden on the notification form whilst subscribable is not. Also it implies users will receive the notification no matter what like force delivery

Mandatory

FALSE

Normal on/off per user subscription allowed

User notification preference can be save as on or off

Force delivery

FALSE

No changes

No changes

Field is hidden. Force delivery may indicate the notification is sent immediately. There is a business rule "Update Email Devices" that makes cmn_notif_device inactive when the sys_user Notification is Disable which makes this option redundant

Force delivery

TRUE

Ignores sys_users Notification value.

User Notification preference is created even if the are not valid recipients of the notification

Users receive the notification even if the user Notification field is set to Disable and their cmn_notif_device is active

 

Here is an example of some email notifications showing the Subscribable, Force delivery and Mandatory option set to true and the others set to false:

 

2015-11-16_2208-force_001.png

Subscribable option set to false

If you want a normal notification, you can set the "subscribable" to false. They use the 'Who will receive' information to generate the recipient list.

They are called subscription-based notifications because they allow users to choose which notifications they prefer to receive and which ones they don't

 

If the 'Mandatory' option is set to true, the notifications will be editable but will not save the "off" value on the user notification preferences. Subscribable alone does not make it mandatory.

Also mandatory respect the sys_user notification field. To ignore that, set 'Force delivery' to true, then users receive the notification even if the user Notification field is set to Disable. These fields only affect the current notification.

 

I have set four (4) users as follow:

User

Sys_user Notification

User Notification preferences

User1

Enable

All notifications enabled

User2

Enable

All notification disabled by setting off the dials

User3

Disable

All notifications enabled

User4

Disable

All notifications disabled by setting off the dials

 

Here is the result of the tests:

Notification

Final email notification

Name

Mandatory

Force Delivery

Original recipient list

Final recipient list

Email Logs shows

No1

False

False

User1

User2

User3

User4

User1

Notification 'No1' included recipients via the notification's "Users" field: 'User1'

 

Notification 'No1' excluded recipients because user's notification preference "Filter" filtered it (see "cmn_notif_message.notification_filter"): 'User2'

 

Notification 'No1' excluded recipients because user's "Notification" setting is disabled: 'User3' (x), 'User4'

No2

False

True

User1

User2

User3

User4

User1

User2

Notification 'No2' included recipients via the notification's "Users" field: 'User1'

 

Notification 'No2' included recipients because users would normally be excluded, but notification's "Force delivery" setting is enabled.: 'User2'

 

Notification 'No2' excluded recipients because user's device is inactive (see "cmn_notif_device.active"): 'User3' (x), 'User4'

No3

True

False

User1

User2

User3

User4

User1

User2

Notification 'No3' included recipients via the notification's "Users" field: 'User2' (x), 'User1'

 

Notification 'No3' excluded recipients because user's "Notification" setting is disabled: 'User3' (x), 'User4'

No4

True

True

User1

User2

User3

User4

User1

User2

Notification 'No4' included recipients via the notification's "Users" field: 'User2' (x), 'User1'

 

Notification 'No4' excluded recipients because user's device is inactive (see "cmn_notif_device.active"): 'User3' (x), 'User4'

 

The 'Who will receive' section should not be EMPTY. If the system is not able to generate recipients, it will not generate an target email.

Example of a Subscription based notification

I created/set 3 users

* aileen.mottern - aileen.mottern@example.com

* alejandra.prenatt - alejandra1.prenatt@example.com

* alissa.mountjoy - alissa.mountjoy@example.com

 

I created/set 1 cmdb_ci_computer

* "*DENNIS-IBM" (sys_id d0e8761137201000deeabfc8bcbe5da7)

 

I created a new email notification test_subscription on incident as follows:

Name

=

test_subscription

Table

=

incident

Inserted

=

checked

Updated

=

checked

Conditions

=

none

Users

=

aileen.mottern

alejandra.prenatt

Subject

=

Test test_subscription

Body

=

Test test_subscription

 

I hardcoded aileen.mottern and alejandra.prenatt as recipients. I filled in the reset of the required fields so it looks like this:

 

hardcoded notif.jpg

 

Then I create a new incident (e.g. INC0010021) to check the notification email results. The results confirm that notifications are sent to aileen.mottern and alejandra.prenatt.

aileen.mottern and alejandra.prenatt can choose not to receive the notification if the mandatory field is set to false

Subscribable option set to true

 

Also called CI-based notifications build their recipient lists by two parts:

  1. It generates a list of recipients like subscription-based notifications (same as with Subscribable set to false).
  2. Plus, the second part which search on cmn_notif_message for users subscribed to the record received on the event as configured in the notification.

They are called subscribable notifications because the option is called subscribable and implies a "subscribe" action  similarly to a subscription business model.


The second part is complex. You MUST set the "subscribable" to true and specify 'Item table' and 'Item' fields in the notification.

Workflow to mantain the connections between the user preference on cmn_notif_message and the records may be required.

The record-subscription-based part, needs users subscribed to the 'Configuration Items' (or relevant column) on cmn_notif_message for the notification. The event itself will specify which specific record to match with the notification preferences.

 

There are a few subscribable notifications already provided on the system.  They have 'Item table' set to cmdb_ci, sys_user, live_message, incident_alert, sys_user_group, cmn_cost_center,etc and the notifications are triggered by "xxx.affected" events.  These notifications do NOT look for the data in the configuration item field on the task record (e.g. incident) as some people with good common sense may think. This is a common misconception. The subscribable notifications on the system look for affected cis related to the task.

 

If you are only interested on the record-subscription part, the 'Who will receive' section can be empty.

 

Example of a Subscribable notification

 

I've changed the 'Subscribable' to true (CHECKED), set 'Item table' = "cmdb_ci", and Item to event.parm1.

 

Name

=

test_subscription

Table

=

incident

Inserted

=

checked

Updated

=

checked

Conditions

=

none

Users

=

aileen.mottern

alejandra.prenatt

Subject

=

Test test_subscription

Body

=

Test test_subscription

Subscribable=checked
Item table=cmdb_ci
Item=event.parm1

 

 

I updated the incident created before e.g. INC0010021 and checked the notification email

Results: The notification is sent to aileen.mottern and alejandra.prenatt.

 

Here is the example of the previous two notification sent:

subscribable notif.jpg

 

Note that the same notification is sent. This is because the "same as Subscription based notifications" is executed. As "subscribable" is set to true, the notification tried to match event.parm1 with the 'Configuration Items' on cmn_notif_message and can not find any matching.

 

In a nutshell, setting the 'subscribable' = true does not affect the recipient list yet.

 

Testing the record-subscription based part of Subscribable based notifications

 

The record-subscription based part needs:

  1. Users subscribed to the 'Configuration Items' on cmn_notif_message for that notification.
  2. The notification to specify which record to match to the notification (e.g. event.parm1).

To make the record-subscription-based work, you need a few users to subscribe to some Configuration Items.

I logged with alissa.mountjoy. Then open "*DENNIS-IBM" on cmdb_ci_computer table and clicked the "Subscribe" link.

subscribe link.jpg

To simplify this example, I opened the cmn_notif_message_list.do record for alissa.mountjoy and changed the Notification Message value to 'test_subscription' (from 'CI Affected').

test subscription.jpg

 

Next, I set up the notification to specify which record to match to the notification (e.g. event.parm1).

To simplify the testing, I've also changed the same notification to be fired on 'ci.affected'.

 

 

Name

=

test_subscription

Table

=

incident

Send When

=

Event is fired

Event name

=

ci.affected

Conditions

=

none

Users

=

aileen.mottern

alejandra.prenatt

Subject

=

Test test_subscription

Body

=

Test test_subscription

Subscribable=checked
Item table=cmdb_ci
Item=event.parm1

 

 

Then, I manually queued the event for that CI.

Note d0e8761137201000deeabfc8bcbe5da7 is the sys_id of the computer system.

 

// Search for the incident to set the event target
var target = new GlideRecord('incident');
target.addQuery('number', "INC0010021");
target.query();

// Queue the event
if (target.next()) {
    gs.eventQueue("ci.affected", target, "d0e8761137201000deeabfc8bcbe5da7", "*DENNIS-IBM");
};

 

The event generated was:

event generated.jpg

As the event.parm1 matches the record on cmn_notif_message for the notification "test_subscription" on the notification recipients, you can see alissa.mountjoy was added.

user added ci.jpg

 

To conclude, normal Subscription based notifications allows you to set the recipients based notifications 'Who will receive' information. This is simple and practical. Subscribable notifications allow you to notify users subscribed to reconds on the system in addition to using the 'Who will receive' fields. Subscribable set to true could add extra unwanted searches if used incorrectly.

 

I have tested the examples with Chrome an Fuji.

 

 

More information here:

Now that we have pretty much nailed down how to send query requests to the ServiceNow out-of-the-box default table WSDL let’s move to something a bit more complex and useful!

 

In this article we will be constructing a simple scripted web service that will take an XML payload, and update a specified record in the CMDB Computer table (cmdb_ci_computer).  This will require, of course, sending the XML via a SOAP Client to our new web service.

 

What is really cool about all of this is that we can set up this inbound XML to be anything we want.  It does not have to follow any OOtB ServiceNow standard.

 

So, for this example I will be adding five new fields to the Computer table to hold additional CPU information that we might be able to garner from any Windows machine.

 

Prerequisites:

 

I highly recommend doing the Part 1 and Part 2 articles before proceeding with the labs in this article.

 

1. Some sort of SOAP Client installed on your test machine. In the lab I will be using SoapUI.

2. Create a web service user with itil and soap roles in ServiceNow.

3. Perform the first lab: Mini-Lab: Web Services – Part 1: Using SoapUI to Test ServiceNow WSDL

4. Perform the second lab: Mini-Lab: Web Services – Part 2: Using an Encoded Query

5. Some knowledge of ServiceNow Scripting would be useful.

6. Some knowledge of Scripted Web Services would be a bonus as well.

Scripted Web Services - ServiceNow Wiki

Scripted Web Service Tutorial - ServiceNow Wiki

 

 

Lab 1.1:  Computer Table Setup

 

There are a couple of ways of adding new fields to an existing table.  You can edit the table definition and add the fields (System Definition -> Tables), or you can go through the Form view of one of the records in that table and add the fields via Form Layout.  Since Form Layout is the faster/easier method we will be using that.

 

1. Log on to your ServiceNow instance.

 

2. Navigate to Configuration -> Base Items -> Computers.  The Computer list view will be displayed.

 

3. Edit one of the records.  The form view will be displayed for that record.

 

4. Right-click on the form view header to display the context menu and navigate to Configure -> Form Layout.  The Form Layout form will be displayed.

 

5. Add the following fields to the bottom of the first form section:

 

CPU Socket

CPU Architecture

CPU Processor Type

CPU Family

CPU Status

 

NOTE: For simplicity make all of these String with a length of 40.

 

 

6. Click the Save button to save your work.  Your form should look something like this:

 

 

7. Grab the sys_id for this record by right-clicking on the form header to display the context menu, and choosing Copy sys_id.  A pop-up window will appear showing the sys_id of the record.  Copy the sys_id that is displayed to your favorite notepad.  We will be using this in our Scripted Web Service.  Close the pop-up window.

 

8. Right click on the field labels for each of the new fields. Note down each of the new field names. We will be using these in our Scripted Web Service as well.  They should look like these:

 

u_cpu_socket

u_cpu_architecture

u_cpu_processor_type

u_cpu_family

u_cpu_status

 

 

That finishes setting up our table to receive the new values we will be sending in. Now we are ready to move to creating our Scripted Web Service!

 

 

Lab 1.2:  Creating the Scripted Web Service

 

1. From your ServiceNow instance navigate to System Web Services -> Scripted Web Services.  The Scripted Web Services list view will appear.


2. Click the New button.  The new Scripted Web Service form will appear.


3. Fill in the form with the following:

 

a. Name: UpdateComputerCI

b. Active: True

c. Short Description: Update custom Computer CI fields

d. Script:

 

try {
   xmldoc = new XMLDocument(soapRequestXML);
   var sys_id = xmldoc.getNodeText("//sys_id") + '';
   var cpuSocket = xmldoc.getNodeText("//cpu_socket") + '';
   var cpuArchitecture = xmldoc.getNodeText("//cpu_architecture") + '';
   var cpuProcessorType = xmldoc.getNodeText("//cpu_processor_type") + '';
   var cpuFamily = xmldoc.getNodeText("//cpu_family") + '';
   var cpuStatus = xmldoc.getNodeText("//cpu_status") + '';
   
   gs.log('--->soapRequestXML: ' + soapRequestXML);
   
   var computerRecords = new GlideRecord('cmdb_ci_computer');
   if (computerRecords.get(sys_id)) {
          computerRecords.u_cpu_socket = cpuSocket;
          computerRecords.u_cpu_architecture = cpuArchitecture;
          computerRecords.u_cpu_processor_type = cpuProcessorType;
          computerRecords.u_cpu_family = cpuFamily;
          computerRecords.u_cpu_status = cpuStatus;
          computerRecords.update();
   }
}
catch (err) {
   gs.log('---> ERROR: ' + err, 'SWS: UpdateComputerCI');
}

 

Leave all the other fields to default.  You could change the name of the function from “execute” to something else, but I felt this was pretty descriptive of what we would be doing so I left it.

 

I put a try/catch around the code to make sure we capture anything bad that might not have been anticipated.  You could always slick this up by adding some checking on the in-bound values.

 

The variable soapRequestXML is reserved/built-in and will automatically contain our entire inbound XML payload. Using XMLDocument we convert the XML to an object then reference the fields we want to extract.  I threw in the gs.log to print off what the XML looks like when it comes in.

 

Finally using the sys_id that was sent to our code we can update the existing computer record with our inbound values.

 

BTW, did you see how the displayed WSDL URL modified itself to contain the name of your Scripted Web Service?

 

e. Right-click on the Form header to display the context menu and click Save to save your work.  This will cause the related fields tabs to appear at the bottom of the form.

 

 

f. Click on the Input Parameters tab, and click the New button. The new Input Parameters form will be displayed.  Here we will be describing our one inbound variable that will contain our variables.

 

g. Fill in the form with the following:

 

i.   Name: payload

ii.  Leave all the other fields their default values.

iii. Click the Submit button to save the new Input Parameter.

iv. The Scripted Web Service form should be re-displayed.

 

 

h. Copy the WSDL URL to your notepad.  We will be using this to verify the WSDL, and in our SOAP Client to test with.

 

4. Open a new browser tab and paste your WSDL URL into the navigation field to see your new WSDL.  Note the inbound payload variable.

 

 

We are done with our Scripted Web Service!

 

 

Lab 1.3: Testing the Scripted Web Service

 

Now as the last step we need to set up our SOAP Client to test our new Scripted Web Service with!  I will be doing this with SoapUI, but you could just as easily set this up with any other SOAP Client.

 

1. Open SoapUI.

 

2. Right-Click on Project to display the context menu, and click on New SOAP Project.  The New SOAP Project form will be displayed.

 

3. Fill out the form with the following:

 

a. Project Name:  Update Computer CI

b. Initial WSDL:  Fill in your incident wsdl url here.

Example: https://dev00001.service-now.com/UpdateComputerCI.do?WSDL

c. Create Requests: Checked

d. Click the Ok button to display the Basic Authentication form.

 

4. The Basic Authentication form will appear.

 

5. Fill out the form with the user credentials you created back in the Part 1 article.

a. Username:  svcuser

b. Password:  your user’s password.

c. Click on the Ok button to create the new Incident project.

 

6. In the left-hand tree view navigate down to and click on result1 to bring up the properties.

 

7. Fill in the following in the properties window:

 

a. Name: Update Computer

b. Username: svcuser

c. Password: the password for svcuser

 

8. From the tree view double-click on Update Computer. This will display the Update Computer XML. 

 

9. In the Update Computer XML window change the XML to look like the following:

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:upd="http://www.service-now.com/UpdateComputerCI">
   <soapenv:Header/>
   <soapenv:Body>
      <upd:execute>
         <payload>
              <sys_id>7f8a10d60fb3ca002ae14e9ce1050e42</sys_id>
              <cpu_socket>CPU Socket - U3E1</cpu_socket>
              <cpu_architecture>x64</cpu_architecture>
              <cpu_processor_type>Central Processor</cpu_processor_type>
              <cpu_family>Intel Core™ i5</cpu_family>
              <cpu_status>CPU Enabled</cpu_status>
        </payload>
      </upd:execute>
   </soapenv:Body>
</soapenv:Envelope>

 

The sys_id is the one you copied from your CMDB Computer record earlier.

 

Since I started out meaning these to be real fields (for the purposes of the next article) I have gone to the trouble here of showing real values that will be stored in the table.  I pulled these manually from here:

 

Win32 Processor Class

 

10.  Everything should now look something like this:

 


And now we are ready to test!!!  Whoop! Trust me; this is worth it! 

 

11.  Now click on the green “start” button at the top of the Update Computer XML form.  This will send our test XML to our new Scripted Web Service.  You should see something like the following when it is done:

 

The response XML indicates a successful run.  If you do not get anything back take a look at the SoapUI log and/or the http log at the bottom (press buttons).

 

Now let’s check to see what happened on our instance!

 

12.  In your ServiceNow instance navigate to System Logs -> System Log -> All

 

13.  Set the filter to where “Message starts with --->”

 

14.  You should see something like this:

 

 

This is our inbound XML captured for our study.  Looks exactly as we sent it doesn’t it?  Well...sort of...close enough!

 

15.  Now navigate to Configuration -> Base Items -> Computers.  The Computer list view will be displayed.

 

16.  Filter on where sys_id equals the value of the record that you copied out in Lab 1.1 above. You should see just your one record displayed.  Edit that record.

 

17.  You should see all of your new fields filled in something like the following:

 

 

That is all there is to it!  You now have a model to play with that sends a simple XML payload into a Scripted Web Service that then updates values in a CMDB Computer record.  Cool, huh?! 

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

Also, if you are not already, I would like to encourage you to become a member of our blog!

 

CS_logo.jpgMVP-logo.jpeg

Please Share, Like, Comment this blog if you've found it helpful or insightful.

 


For other Expert Knowledge

Ask A Developer       What’s In Store For IT Leaders in 2016       ServiceNow Online Learning Courses      ServiceNow Foundations: User Interface


 

The notification from the ServiceNow UI can be extremely flexible with configuration. With all of the configuration capabilities and platform flexibilities for creating email notifications, you can end up overlooking how complex notifications can be. Notifications are created with a purpose to "inform about an event" and they are happily self-contained to integrate with the systems. However, this independence and simplicity can cause oversight on them triggering the same message.

 

949.1662-Spam1.jpg

 

Lets talk about

  • The problem with events stacked that send the same notification
  • Example of events stacked sending the same notification message
  • Using event parm1 values to overcome events sending the same notification message
  • Using the notification weight to overcome duplicated email

 

Most events are not processed straight away. Events will queue up and they have to hold until previous events are processed. Once the previous events are processed, then the following events can fire. The sysevent table holds the event queue generated by the system.

 

Events allow you to specify:

  • Event name
  • Target record
  • Two parameters

 

They have four states:

  • Ready
  • Processed
  • Error
  • Transferred

 

Events named 'notification_engine.process' are Engine based. Any other event, not named 'notification_engine.process' is considered Event based. A simplified version to explain the event life cycle looks like this:

 

Delay

Event cycle

Data changes

Events are created in "Ready" state

Record expected by event is accurate

#1 Event is queued – this is the most likely delay

Events WAIT - this could be seconds, or minutes if there are previous events or the event processor is stopped, etc

Users or workflow may change the record

Processing start

Events are 'claimed' by a node, then set to "processed" state when completed

If they trigger email notifications, the conditions are evaluated here with the ‘current’ record*

*Record can have changed by this time

#2 Delay depends on the number of recipients

Then email notification is processed to generate the recipients

Users or workflow may change the record

Then generate the notification message with the ‘current’ record**

**Record can have changed again by it is less likely

Processing ends

Processed time is set

 

 

The Event Processor in the ServiceNow system is in charge of handling the events that come in. Think of it as the swiss army knife of the notification system. Being that the Event Processor has several actions to review, manage and fire, like any assembly line, if it gets too backed up it could cause performance issues. If the event processor is stopped, or busy, it can cause a long queue list of events. As default, every 30 seconds gs.eventsProcess() runs on each node and clears the 'ready' events.

You recognise this problem because the notification usually has a conditions with data is likely to change (e.g. conditions on assigned group, assigned to, etc.) and it's usually the source of the triggering event itself (e.g. incident.assigned)

The source of the problem with events stacked that send the same notification is that there is a delay between event creation and the event being processed. There is a tiny chance of delay on recipient creation. By the time the notification conditions are processed there is a chance the current record could have changed. The problem here is that an event that should have not passed the conditions, will later pass them because the new values passes the notification conditions. In most cases the result is the same message send twice.

 

Here is an example of events stacked sending the same notification message:

To better articulate why duplicate notifications due the event stacked is an issue, and passing outdated records through validation is bad, I have put together an example.

 

I have set three(3) users:

  1. Alejandra Prenatt - Sysid = 22826bf03710200044e0bfc8bcbe5dec
  2. David Loo
  3. Charles Tylor (Duty Manager).

 

I created a test notification that should only fire with assigned to is "Alejandra Prenatt":

 

Name

=

test_notification_01

Table

=

Incident

Event name

=

incident.assigned

Condition

=

assigned_to = "Alejandra Prenatt"

Subject:

=

This incident Number: ${number} - Assigned to: ${assigned_to}

Message

=

Assigned to: ${assigned_to}

Here is how the notification looks:

event stacked notification.jpg

 

I tested it by creating the two quick changes in incident:

  • Then go to Incident table and assign an incident to "David Loo" (internally, event 1 fires)
  • then quickly assign it to "Alejandra Prenatt." (internally, event 2 fires)

 

You will receive two notifications with the same subject and recipient is "Alejandra Prenatt." Reviewing the logic, you can see the first update 'Assigned to' = "David Loo" (event 1) on which the email notification SHOULD HAVE NOT PASSED. However, another update with 'Assigned to' = "Alejandra Prenatt" happens (event 2). In practice, there is a delay for the event to be processed, the current record 'Assigned to' = "Alejandra Prenatt" which now matches the condition and when processing event 1 and 2, it sends both notifications.

 

same notification passed.jpg

 

This is a normal but unwanted behavior. Keep that in mind when designing notifications.

However, there are multiple workarounds.

One solution is to create specific events that get used on those specific cases instead of conditions based on data that may change, then limit the notifications to those events

Workaround #1: Using event parm1 values to overcome the events sending the same notification message

You can modify the email notification condition to validate by the data passed on event.parm1 or event.parm2.

 

Here is an example, changing the notification advanced condition:

Advanced condition =

checkAssignedToUser();
// Check if the parameter provided is the user
function checkAssignedToUser() {
    var vbool = (event.parm1 == 'Alejandra Prenatt');
    return vbool;
}

 

Here is how it looks in the notification:

overcome events.jpg

I tested it by creating the two quick changes in incident: Then go to Incident table and assign an incident to "Charles Tylor (Duty Manager)," then quickly assigned it to "Alejandra Prenatt."

 

Two events were generated on 'incident.assigned.' One contains parm1="Alexandra Prennatt" and the other parm1="Charles Tylor (Duty Manager)." Even when current.assigned is "Alexandra Prenatt," the event that has parm1 parameter = "Charles Tylor (Duty Manager)" does not match the condition and it does not generate a notification, so only one notification is sent to "Alejandra Prenatt."

 

 

Workaround #2: Use the notification weight to overcome duplicated email

Another option is to set the weight to a value higher than zero (0). By setting the weight to a value more than zero, it will cause the business rule to "Ignore duplicates" on the sysemail table to trigger and send any duplicated emails into the ignored mailbox. This options is easier and cleaner than the previous workaround but it generates an unwanted email in the ignored mailbox.

 

On my case, I set the weight to 10. Then retested,

email weight.jpg

 

 

I tested it by creating the two quick changes in incident:  Then go to Incident table and assign an incident to "David Loo," then

quickly to "Alejandra Prenatt."

The system generated two emails to "Alejandra Prenatt." However, only one email remains on ready to send. The other was sent to the ignored mailbox.

This only works if both emails are in 'ready-to-send' state at the time the last event is processed.

 

2015-11-02_2206-Stack08.png

 

 

In conclusion, please be aware of the time gap between when the event was generated and when the event was processed. This is normal behavior that can cause unwanted results like notifications that look like duplicates.

 

 

More information can be found here:

In my previous article we used a SOAP Client to send a simple query to ServiceNow to retrieve a set of records.  Building on what we did in that article I wanted to next tackle encoded queries.

 

In reality this isn’t much different than GlideRecord encoded queries. ServiceNow has provided in each out-of-the-box table WSDL an encoded query parameter that allows you to do essentially the same thing.

 

Prerequisites:

 

If you have already performed the lab in my previous article you can skip the prerequisites.

 

1. Some sort of SOAP Client installed on your test machine. In the lab I will be using SoapUI.

2. Create a web service user with itil and soap roles in ServiceNow.

3. Perform the first lab: Mini-Lab: Web Services – Part 1: Using SoapUI to Test ServiceNow WSDL

 

 

Lab 2.1: Executing an a Date Range Query With a SOAP Client

 

In this lab we will be pulling back a set of records based on a date range.

 

1. Using our previous SoapUI project; edit the XML to look like the following:

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inc="http://www.service-now.com/incident">
   <soapenv:Header/>
   <soapenv:Body>
       <inc:getRecords>
           <__encoded_query>sys_updated_on&gt;=2015-05-10 00:00:00^sys_updated_on&lt;=2015-11-20 23:59:59</__encoded_query>
       </inc:getRecords>
   </soapenv:Body>
</soapenv:Envelope>

 

NOTE:  This will retrieve all Incident records that were updated between the two dates mentioned.

 

2. Click on the play button to run the query.  You should see your results appear in the results panel to the right of the query.

 

3. Look through the results and make sure that the updated date falls within the range specified in your query.

 

As you can see the encoded query is actually pretty straight-forward.  It is knowing the syntax that is secret.

 

Now let us try something a bit more complex.

 

 

Lab 2.2: Executing an Encoded Query With Additional Filters

 

Here we will pull back all records that are Active and were the Assigned To user ID is either Beth Anglin or David Woo.

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inc="http://www.service-now.com/incident">
   <soapenv:Header/>
   <soapenv:Body>
       <inc:getRecords>
          <active>true</active>
          <__encoded_query>assigned_to.user_nameINbeth.anglin,david.loo</__encoded_query>
       </inc:getRecords>
   </soapenv:Body>
</soapenv:Envelope>

 

NOTE: A couple of things here.  Note that you can mix in the fields from the WSDL; as in this case here the active field is used.  This, of course, could just as easily been added into the encoded query, but I wanted to show what was possible.  Also note that the assigned_to field is actually the sys_id of a user.  So it will be necessary to dot-walk to the user id field (user_name) to get at the real-English names.

 

 

Encoded Query Reference:

 

I thought I would throw in a “non-comprehensive, but overall pretty good” reference of what I have tested and found to work with the encoded query parameter.

 

  • These are all URL encoded queries.  It is not necessary to encode everything.
    • Dates are any valid date format.  I prefer: yyyy-mm-dd
    • Dates and strings are not to be surrounded by quotes. Exception: When working with JavaScript values.
    • The ">" symbol is encoded as &gt;
    • The "<" symbol is encoded as &lt;
    • Less than or equal to: &lt;=
    • Greater than or equal to: &gt;=
    • AND is represented by "^"
    • OR is represented by "^OR"
    • Booleans:  falsetrue
    • NOT is "<>"  and is indicated thus &lt;&gt;
    • Dot walking is allowed!
    • NOTE: It is not allowed to use parenthesis in encoded queries.

 

  • AND Query

 

sys_updated_on=2015-05-10^sys_updated_by.user_name=Admin

 

This would return all records updated by user Admin on 10/5/2015.

 

  • OR Query

 

sys_updated_on=2015-05-10^ORsys_updated_on=2015-05-15

 

This would return all records updated on 5/10 and 5/15/2015.

 

  • IN Query

 

assigned_to.user_nameINbeth.anglin,david.loo

 

  • CONTAINS Query

 

sys_updated_byCONTAINSbeth.anglin

 

  • Starting a NEW Query (NQ)

 

sys_updated_byCONTAINSbeth.anglin^NQsys_id=5137153cc611227c000bbd1bd8cd2007

 

  • LIKE Query (end wild card is implied)

 

sys_updated_byLIKEbeth.a

 

  • ROLE Query

 

roles=itil

 

  • Choice Query (by value field)

 

install_status=1

 

  • Javascript parameters are allowed

 

sys_updated_on&gt;javascript:gs.dateGenerate('2015-05-10’,'23:59:59')

 

  • ORDER BY Query

 

unit_name=day^ORDERBYvalue

 

  • BETWEEN Query

 

What did work:

 

sys_updated_on&gt;=2015-05-10 00:00:00^sys_updated_on&lt;2015-05-20 23:59:59

 

 

What did not work:

 

Interestingly I was not able to get BETWEEN to work and by all accounts it should have.

 

sys_updated_onBETWEEN2015-05-15^2015-06-15

 

sys_updated_onBETWEEN2015-05-15,2015-06-15

 

sys_updated_onBETWEENjavascript:gs.dateGenerate('2015-11-01','00:00:00')@javascript:gs.daysAgoEnd(0)

 

 

  • INSTANCEOF Query

 

sys_class_nameINSTANCEOFcmdb_ci_computer

 

NOTE:  This query replaces:

 

sys_class_name=cmdb_ci_computer

^ORsys_class_name=cmdb_ci_server

^ORsys_class_name=cmdb_ci_win_server

^ORsys_class_name=cmdb_ci_unix_server

^ORsys_class_name=cmdb_ci_linux_server

 

 

Some great references:


https://wiki.servicenow.com/index.php?title=Direct_Web_Services

http://wiki.servicenow.com/index.php?title=Direct_Web_Service_API_Functions

 

There you go!  Now you can see how encoded queries can be used by an external Soap Client to retrieve information from a ServiceNow table.

 

In my next article I will show how what we have covered so far with WSDL and Soap Clients can help with the creation of ServiceNow Scripted Web Services.

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

Also, if you are not already, I would like to encourage you to become a member of our blog!

CS_logo.jpgMVP-logo.jpeg

Please Share, Like, Comment this blog if you've found it helpful or insightful.

 


For other Expert Knowledge

Ask A Developer       What’s In Store For IT Leaders in 2016       ServiceNow Online Learning Courses      ServiceNow Foundations: User Interface


 

For a while I have been contemplating a series of articles on how to interface externally with ServiceNow using Scripted Web Services.   In this first article I will be showing how to use a SOAP client to talk with a ServiceNow WSDL.   

 

 

Soap Client: SoapUI

 

I have been using SoapUI for almost as long as it has been around.  I was looking for a decent Soap client tool and it was recommended to me by a friend. Since it is what I am most familiar with I will be using it in this example.  Btw, in its latest couple of incarnations it does REST as well. Download from:  SoapUI. Follow the installation instructions, and you will then be ready to walk through this short lab.

 

Disclaimer: This is not an endorsement of the SoapUI product by Cloud Sherpas, Inc..  This is simply an example of how to test ServiceNow SOAP/WSDL using this tool.  This is simply my personal preference.

 

Other SOAP Client tools to consider:

 

Membrane

XMLSpy

JMeter

Mooza

 

That is just a sampling.  There are a plethora of them available on the web!

 

 

Lab 1.1: Retrieving an Incident Record

 

So what is involved? 

 

1) We must create a new web service user that will be used to access ServiceNow.

2) Next we have to create a group that will have the correct permissions, and then assign our new user to the new group.

3) We must verify access to the WSDL via our favorite browser.

4) We then fire up SoapUI and create a new project to retrieve incidents.

 

 

1. Log onto your instance.

 

2. Navigate to User Administration -> Users.  The Users list view will be displayed.

 

3.Click the New button.  The new user form will be displayed.

 

4. Fill out the form with the following:

    1. Name: svcuser
    2. Password: pick your favorite
    3. Web Service Access Only: checked.
    4. Accept the default for all other fields.
    5. Click the Submit button to save your work.

 

1.New Web Service User.png

 

5. Navigate to User Administration -> Groups.  The Groups list view will be displayed.

 

6. Click on the New button.  The new group form will be displayed.

 

7. Fill out the form with the following:

    1. Name: Web Service
    2. Description: Contains users used by web services
    3. Right-click on the form header to display the context menu and Save.

2.New Web Group.png

d. Scroll to the bottom of the form to the Related Links tabs, and click on the Roles tab.

e. Click the Edit button.

f. Add the following Roles:

      1. Itil
      2. Soap
      3. Click the Save button to save your work.

 

Without it you won’t be able to retrieve records from those tables. The Soap role allows our web user to access everything.  In addition we have to add the itil role to be able to access and read Incident, Problem, and Change. 

 

3.add itil and soap roles.png

 

g. Click on the Group Members tab, and click the Edit button.

h. Add the following User:

    1. Svcuser
    2. Click on the Save button to save your work.

 

4.Add svcuser.png

          You will see a series of blue informational messages showing that the itil and soap roles have been assigned to your user.

 

          And we are done with the ServiceNow configuration side. We are all set to start doing queries from our external SOAP Client!

 

8. Open a new Tab in your browser, and enter your instance’s url, remove /navpage.do, and tack on: /incident.do?wsdl. The url should look something like this:

    1. https://dev00000.service-now.com/incident.do?wsdl
    2. Hit enter to view the Incident table’s WSDL.

 

NOTE: ServiceNow out-of-the-box exposes most table’s wsdl. This includes several commands with fields that can be utilized.  We will be focusing on the getRecords command.  You can find this by scrolling down a ways, and will look something like this:

 

5.WSDL - getRecords.png

 

These commands allow and authorized SOAP Client to perform actions against the given table.

 

9. Now open up SoapUI.  On the first time it is opened you will see the project work area looking something like this:

 

6.SOAP UI main screen.png

 

10. Right-Click on Project to display the context menu, and click on New SOAP Project.  The New SOAP Project form will be displayed.

 

7.New SOAP Project_1.png

 

11. Fill out the form with the following:

a. Project Name: Incident

b. Initial WSDL: Fill in your incident wsdl url here.

Example: https://dev00000.service-now.com/incident.do?wsdl

c. Create Requests: Checked

d. Click the Ok button to display the Basic Authentication form.

 

8.New SOAP Project_2.png

 

12. The Basic Authentication form will appear.

 

13. Fill out the form with your new user credentials.

    1. Username: svcuser
    2. Password: your user’s password.
    3. Click on the Ok button to create the new Incident project.

 

9.soapui_basic authentication.png

 

NOTE: If you get an error then try a couple more times.  If it still fails you may need to exit Soap UI and retry.  I’ve bumped into this a couple of times.  Not sure why.  Perseverance rules!

 

14. Soap UI does all the work for us by creating a new project with all of the commands, and the base XML we can use to submit our queries!

 

15. Navigate to Projects -> Incident -> ServiceNowSoap -> getRecords and expand the “+” symbol.  Select on Result1 which will be displayed.  This will show the properties window below.

10.new incident project loaded.png

 

16. Fill out the properties window with the following:

    1. Name: getIncidents
    2. Username: svcuser
    3. Password: your user’s password.

 

NOTE: It is necessary to re-fill in the user name and password for each command even though we entered it once already to create the project.  Soap UI does not assume the user name and password used to create the project will be the same used to do the queries.

 

11.getIncident authentication.png

 

17. Double-click on getIncidents to bring up the default XML and results window. 

 

12.getIncidents command xml.png

 

NOTE: The XML displayed is just a template, and will not retrieve anything "as-is".  We will need to modify it in order to actually do a query.

 

18. Copy the XML out to your favorite editor (to keep it around).

 

19. Change the XML to the following:

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inc="http://www.service-now.com/incident">
   <soapenv:Header/>
   <soapenv:Body>
       <inc:getRecords>
           <active>true</active>
       </inc:getRecords>
   </soapenv:Body>
</soapenv:Envelope> 

 

NOTE:  If you have a lot of incidents you may want to use additional fields to narrow the search. Each parameter you add will be ANDed to the previous.

 

20. Click on the Start button in the upper left corner of the XML form.  This will run our query.  After a few seconds the results will appear in the right side (results panel).

 

13.get all active true.png

 

21. Scroll through the results to verify that multiple records were returned.

 

A good reference for ServiceNow SOAP and WSDL can be found here in the wiki.

 

And that is all there is to it! This completes the set-up of our testing environment for SOAP queries.

 

In the next article I will be showing how you can use encoded queries to retrieve specific sets of records.

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

Also, if you are not already, I would like to encourage you to become a member of our blog!

CS_logo.jpgMVP-logo.jpeg

Please Share, Like, Comment this blog if you've found it helpful or insightful.

 


For other Expert Knowledge

Ask A Developer       What’s In Store For IT Leaders in 2016       ServiceNow Online Learning Courses      ServiceNow Foundations: User Interface


 

In Geneva, the script editor has been given significant upgrades in functionality. As you edit scripts, you’ll see a number of changes that make the developer experience smoother and more productive.

 

Real Time Syntax Check

 

One of the first things you will notice as you edit scripts is that there is now a real time syntax checker. This can be toggled on and off with a button. Rather than the manual syntax check via button or the automatic check on submitting the form, you get real time highlighting of syntax warnings and errors in you code. As you type, the bar on the left side will display line numbers as well as noting lines with syntax errors.

Screen Shot 2015-11-18 at 10.33.56.png

 

By hovering over the dots, you will see the text of the error message. The Javascript compilation is happening in the background as you type and giving you feedback about lines of code that need to be fixed. As you continue to type and fix the errors, you will see the indicators disappear.

 

 

Autocomplete Known Record Types

 

New in Geneva is autocomplete in the script editor. In any situation where the editor can determine what your object is, it will autocomplete fields and methods of that object. For example, using the Marketing Events application from the developer program training courses as an example, here is the editing of one of those business rules. After a rule has been saved (and it has to have been saved first), the editor now understands what table it is acting on so it can determine what “current” is. This rule is acting on the Marketing Event table, so typing an “e” character shows the available fields that start with that letter. The editor autocompletes functions as well, which you can note by the “F” icon to the left.

Screen Shot 2015-11-18 at 10.43.24.png

 

One word about the limitation of auto-complete: as of Geneva you cannot dot-walk through reference types and have them complete each one. If your record has a Reference type field, you can complete that but not the fields of the referenced type. As an example, if you try to auto-complete “sys_updated_by” on a record, you will not be able to auto-complete the fields from the sys_user table directly.

 

 

Autocomplete Any Object Type

 

The autocomplete doesn’t end at the inferred object types. If you define an object in code, the editor will be able to auto-complete in that context. In my example, I am creating the variable named object, which contains two data fields and a function definition. If we then reference object and auto-complete, it shows the three available possibilities - the two data fields and the function. In this case, it even displays a different icon for the numeric field versus the field of string type.

 

You’ll note that as you auto-complete on any GlideRecord type object, all the data fields have an “O” icon even if it is a String type variable or Numeric. The reason for that lies in the way GlideRecords are implemented. Although they are typed, at their heart every data field on a GlideRecord is an object of type GlideElement so that is why they register as objects and not as data fields. Any object defined by you in your scope will display the type specific icons.

Screen Shot 2015-11-18 at 12.42.34.png

Note that you can even autocomplete the GlideSystem functions just by typing “gs”. Since the editor knows what type of object gs is, you see the autocomplete information as soon as you type the following dot. If you are ever in a situation where the autocomplete is not popping up but you want it, such as if you used the arrow key to navigate around, you can type Crtl-space to bring it back.

Screen Shot 2015-11-18 at 10.48.53.png

 

 

Hotkeys

 

That leads us into the next topic - hotkeys in the script editor as of Geneva. At any point you can click the question mark icon and it will pop up a list of the editor hotkeys.

Screen_Shot_2015-11-18_at_13_08_16-with-box.png

 

Since the Fuji release, almost all of the hotkey combinations have changed for the simpler. For example, in Fuji to comment code was Ctrl-Alt-C and to uncomment was Ctrl-Atl-U. As of Geneva, Cmd-/ will toggle the comment, so the single combination will add or remove a “//“ style comment prefix from a single line or even a selected block.

Similarly, searching in the script editor has been simplified to Cmd-F to start searching, Cmd-G to find next and Cmd-Shift-G to find previous. This brings the keystroke combinations more in line with what developers would expect from using other similar tools. It makes the experience of authoring code in the script editor more intuitive. As with many of the Geneva enhancements to the development environment, it brings the ServiceNow platform closer to parity with the IDE style tools a desktop developer would be familiar with.

Screen Shot 2015-11-18 at 13.07.11.png

 

Autosave

 

One additional feature that is new to Geneva is autosave. As I was writing this blog post, I had a runaway Chrome page that forced me to close my whole browser window. When I returned to my business rule to continue editing, you’ll notice the icon in the upper left corner. That shows that this particular application file has auto-saved data that has not been saved to the instance. Although I had not saved the example of creating the local object, even after a browser crash when I reloaded the “Calculate total cost” business rule, the edited code was inserted back into the script field. This is not a feature I expect developers to see a lot (at least I hope not) but on those occasions when it is triggered, it will be a great time saver.

Screen Shot 2015-11-18 at 12.40.21.png

 

As you can see, the Geneva release brings many great features to help developers get their job done faster, with less error and lower risk of losing work.

 

Other entries in this series:

 

What's New in Geneva for Developers? - the overview post

What Developers Need to Know about Geneva: Part 1 - Studio

Dave Slusher | Developer Advocate | @DaveSlusherNow | Get started at https://developer.servicenow.com

New in Geneva is ServiceNow Studio cloud IDE - that transforms developer productivity by delivering a familiar experience that developers expect with all tools in one place. Features like Application Explorer, Go To File Search, Code Search, intelligent scripting, and tabbed development make app dev on ServiceNow faster and easier so developers can get more stuff done faster. You can publish from Studio to your test and production instances and even to the ServiceNow Store, and easily manage upgrades via the app repository.

 

Studio is 100% cloud based so there's nothing to download, install, configure, or maintain - it all happens in the browser. And when ServiceNow upgrades Studio, you get it for free, automatically, without doing anything.

 

Go_to_file_search_Studio_IDE.png

 

 

 

You can learn more about Studio in this blog post.  Start developing where you belong - in the Enterprise Cloud. You can upgrade your developer instance to Geneva and get cracking today.

Martin Barclay
Director, Product Marketing
App Store and ISVs
ServiceNow
Santa Clara, CA

Have you ever made modifications to your instance only to realize that those mods were being placed into the Default update set and not into the one you thought the changes were supposed to go to?  Has it caused you to beat your fists on your keyboard until the keys fly off and you spend an hour searching around all the books and clutter on your office floor retrieving them?  Has it caused you to seek out a psychoanalyst?  Well!  Help is here!

 

To begin with you could move your changes using the normal method of simply editing up the Customer Change record and specifying the update set you want to be in.  While this is great for one or two updates; it is totally untenable for more than that.  I have zero desire to spend an afternoon moving all those records!

 

 

However, there is a way of mass moving change records, but you have to know where to look, and what you are about.

 

 

Lab 1.1: Moving Customer Update Records

 

1. Navigate to System Update Sets -> Local Update Sets and click the New button on the Update Sets List View.

 

2. Name it something arbitrary.  This is for demonstration purposes only.  Do not make it your current update set.

 

 

3. Make sure that your current update set is Default - Global.

 

 

4. Go make some modifications - easiest would be to modify a Form or List View.  Do two or three of these so as to show them up in the List View of Default. I modified the list views to add the Due Date to the Incident, Change, and Problem list views.

 

5. Go to the Global Default Update Set.

6. Verify the changes are listed there.  I want to emphasize that you cannot move them through the Customer Updates List View in the Update Set.  You would have to do it one at a time from this location.

 

7. Right-Click on the Customer Updates List View column header to bring up the context menu.  Click on Dictionary.

 

 

8. This will show the name of the table we want to go to: sys_update_xml

 

 

9. From the Navigation Filter type: sys_update_xml.list to bring up the Customer Updates table.  This will display the Customer Updates List View.

 

 

10. If the Comments, Updated, and Updated By fields are missing then personalize the List View to add them.  Order the list by Updated date descending.

 

 

11. Note that your modifications for Default are listed.  Filter for the Default Update Set only; to get rid of the clutter.

 

12. Mass modify the comment field for those records you want to move to your new Update Set.  Something like: Update Set Demonstration

 

 

13. Now mass modify the Update Set field and change it to your new Update Set.

 

 

14. Go to Update Sets and edit up your new one.  Verify that the records were indeed moved to your new Update Set.

 

 

There you have it!  That is the simple recovery.  Now for the one that proves to be a bit more troublesome!

 

 

Lab 1.2: Dealing With Duplicate Records

 

If a change already exists in the update set you are moving to; the one you are moving may be overridden.  Keep your wits about you and you won't have a problem, but it is good to review things after you do your move.  The best way to keep out of trouble is to use the Comment field method I mentioned here and in my previous article.

 

Steps:

 

1. Make sure you are still on the Default Update Set.

 

2. Go make another change to the same List or Form View that you did earlier.  This makes a "duplicate" change to the same element that is already in your new update set, but was recorded in the Default Update Set.

 

3. Return to sys_update_xml.list.  Make sure you are ordered by Updated descending.

 

4. Note the values in the Type and Link columns for your most recent change.  Filter the list view on that Type and Link.

 

5. You should now see two records at the top.  One for your most recent change with the Update Set value of Default, and another older entry that appears to be the same change, but with the Update Set value of your new update set.

 

 

6. So now you can see the issue.  I usually set things up so I am looking at both Default and my new update set entries at the same time.  It helps.  Especially with the comment fields filled in.  So what to do?  As you know I really HATE deleting records, and prefer to hang onto them if at all possible.  You just never know if you are going to need that deleted information back, and once deleted it is gone forever.  So decision time.  I know that the latest modification is the one I want in my update set, but the now duplicated older record could cause a problem.  Since the sys_ids are different ServiceNow will let you move the record to your new update set, but that won't do!  You don't want both of them executing when you move your update set to another instance!  So a couple of possibilities:

 

1) Delete the older record, and move the new record to your update set. Not my favorite.

2) Export the older record to your hard-drive, delete it, and move the new record to your update set.  Well, it does preserve a backup, but still not optimal.

3) Create a new, temporary update set and merge all of your new changes with your new update set; letting ServiceNow sort out the change order.  This is my preference even though it can be a bit tedious if there are several of these.

 

So, let's go with number 3.

 

The process:

 

1. Create a new temporary update set.

 

 

2. From sys_update_xml move the duplicate records from Default to your temporary update set.

 

 

3. Go to your "temporary" update set, and merge it with the "new" update set to create yet another "even newer" update set!  Hey! You are the one that created the mess in the first place!  :-)

 

And then:

 

 

The merged updates set.  Note that the record without the comment is the newer one so replaced the original:

 

 

NOTE: This is just a demonstration.  If this had really been a single record I probably would have gone with option 2.

 

4. Delete or Inactivate your the two update sets ("new" and "temporary").  You will now be moving forward with the "merged" update set.  Make it current, and you are good to go.

 

 

Recap

 

1. Remember to double-check your update set, and make sure it is not Default when making changes.

2. That failing and you end up with records in the Default that you wanted in your other update set you now have a method for moving them.

3. If you have it happen a second time, shame on you, and you find you have duplicates then you have a method for recovering from that mess as well, and hopefully you learned your lesson!

 

Oh, and why do I know how to do all of this?  Because number 3 keeps happening to me!  :-)

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

Scoped Applications are a fun topic for me. I was able to contribute during the development of the Scoped App model, and since then I've written a lot of small Scoped Apps personally, as well as contributed to the Studio- a Scoped Application that's like an IDE for building Scoped Applications (read more about that here).

 

What is a scope?

Generally speaking, a scope is like a namespace for your application. Everything in your application falls under this namespace. It separates your classes, tables, and UI components from everyone else's; it gives you control over their names, how they can be accessed or extended by others, and keeps your code from accidentally polluting the global namespace. A good discussion of these benefits can be found here.

 

Technically speaking, scopes are a bit more complex than that. A scope isn't just a namespace, it's also a record in a table (sys_scope). All of the files that are part of your application have a reference to this record- or to one of the tables which extend it (sys_app and sys_store_app). That's not the true root (sys_package is) but it's as top-level as you ever need to go.

 

All files in your Scoped Application will have a reference to your sys_scope record. That's because every record in your application will be in a table that extends sys_metadata, or will be a table definition (a sys_db_object record and/or sys_dictionary record) which itself extends sys_metadata. In the sys_metadata table, there is a reference field to sys_scope, and this ties your whole app together.

 

The global scope is also a scope- you can check that out too. It's a sys_scope record, where the scope name is 'global'.

Screen Shot 2015-11-18 at 10.12.49 AM.PNG

 

Everything created pre-Fuji is pretty much a part of the same legacy globally-scoped application. Most of the legacy stuff defaults to being open to all app scopes to use. It's also possible to create new Scoped Applications where the scope is 'global'. I don't really see much of a reason to do that, though.

 

When checking things like cross-scope access protections, global is "just another scope". If you create a Script Include in your Scoped App that is "Accessible from: This application scope only", then a script running in global cannot access it. Only scripts in your same scope will be able to make use of it, which means you can make changes in each release without worrying that you might be breaking something else that depends on it. Of course- it cuts both ways. If you make the include accessible to other scopes- like you would do for an API, or some common utility script that you think will be useful, then you should be just as careful as ever about making breaking changes.

 

Screen Shot 2015-11-18 at 10.16.24 AM.PNG

 

 

Differences between Scoped Apps and Global Apps

Update Sets

Global Apps are exclusively tracked in Update Sets. When you want to start work on a new release, you have to create a new, clean Update Set, switch into it, and be meticulous about switching out of it when making non-relevant updates, and switching back into it again when you're done. You have to close the set, transfer it, preview it, fix any problems, and apply it.

 

Scoped Apps can be tracked in Update Sets- they can even be moved around via Update Set, but that's a really legacy way of doing it. We automatically create an Update Set for each scoped app, and switch into it whenever you start editing that app. It's generally something you can ignore. Global Apps use Update Sets as a system of record for changes to an instance, and for moving a series of changes between instances. Scoped Apps are always complete- they represent an "end state" instead of "a series of changes to make". It's not impossible to do the same thing with Update Sets, but anyone who's had to go through every single record they believe constitutes their application and "touch" them to get one complete Update Set out of it knows how annoying that can be. I know it- I've done a few times, and I consider it the nuclear option for fixing "my change isn't coming across".

 

Scoped Apps are packaged up complete, with all relevant records to install on a new instance that has never seen the app before, or update an existing instance where an old version is installed. Even if you choose to Publish to an Update Set, the entire app will be copied in, not just the changes you've made since the last time you published. When you publish to the repo (either via "Make App available on other instances" or "Publish to Store"), the app is packaged up, stored on the repo, and (potentially) made available to all of your organizations instances to download and install or upgrade. Published apps always represent an end-state, not a series of changes to be made.

 

Install and Uninstall

Installing a scoped app is generally as easy as going to the Applications page, switching to the Downloads tab, and clicking the Install button. It's an actual installation, versus applying an Update Set, which is how a legacy application is installed.

 

Screen Shot 2015-11-18 at 10.30.01 AM.PNG

 

Global applications can't really be "uninstalled". Since nothing ties your application together into one cohesive app, it's difficult to tell where your "loaner approval" ends and CMDB and Approvals begin. You can back out Update Sets in reverse order, until you've removed the one the one that brought in your changes, but that's neither fun nor easy. You could also comb through all of the references in your Update Sets to find the individual files, or trace code paths and refer to documentation, but that's harder than it should be. Scoped Apps are uninstallable. You can do it right from the sys_store_app record for that app. I think it's two clicks and a confirmation (you have to manually type "uninstall"). The "everything is packaged together" aspect makes it *vastly* easier to install and uninstall.

 

Screen Shot 2015-11-18 at 10.35.22 AM.PNG

 

Public and Private

Global Apps live together, but not always harmoniously. When everything is a public API, nothing is reliable, or everything must be static. If you write a Script Include with 10 useful methods that get used in a hundred different places, changes to those methods can have far-reaching and unintended consequences. You had to do extensive searching to see if any of the server-side script fields that could potentially access that include actually do, and if your change will adversely affect them (not to mention Filters, default dictionary values, forgotten scheduled jobs, etc).

 

Scoped Apps allow you to set a Script Include private- truly private, accessible only to your application. You can make a public, static API (or one that that at least changes infrequently) while updating the behind-the-scenes private stuff with weekly releases. You can also name it anything you like- a scoped Script Include is automatically namespaced to your scope. Your JSONUtil include can't conflict with some else's, nor with a global one. You can split your utilities from the rest of your code and update them separately.

Screen Shot 2015-11-18 at 10.32.17 AM.PNG

 

It's possible to get most of the benefits of Scoped Apps with the legacy global model- you just have to try hard. It's not easy, and you have to be meticulous. The legacy system promotes interdependence; Scoped Apps promote interfacing. In general, I think we'll all find that route leads to cleaner code, easier updates, and less frequent behavioral anomalies (my new favorite term for 'bugs').

 

Philosophy of Scoped Apps

This is my understanding of the philosophy of Scoped Apps. I had a small part in the platform side, and I don't speak for everyone, but here's how I see it.

 

  • Do not break system functionality.
  • Do not break other apps.
  • Do not hog resources.
  • Be self-contained, with clear dependencies where necessary.
  • It's easier to start locked-down and gradually open up than the reverse.

   

Almost everything we've done with Scoped Apps is to address or encourage those points. For example, you cannot make a field on someone else's table disabled or read-only. If they have marked the table extendable, then you can extend it and make all the inherited fields read-only if you wish. That is one way we try to ensure that apps don't interfere with each other. Another example: GlideAjax calls from scoped Client Scripts must be asynchronous. A popular table like Incident is likely to attract many Scoped Applications, adding Client Scripts and validations and nice conveniences. Since you (the app developer) aren't necessarily aware of the exact environment your Client Script is running in- especially with regard to other Scoped Apps installed alongside yours- it's one way we can make sure that the user isn't stuck waiting for multiple round-trip GlideAjax calls to complete when they attempt to submit a form. In the legacy system, the administrator would need to keep tabs on that.

 

By adhering to the above principles, multiple Scoped Apps, from different vendors who may never have even known about each other, can coexist on the same system. You can release as frequently as you like, and so long as you follow best practices around you public-facing interfaces, you can be much more sure that your release can't break someone else's app, nor their releases break yours.

 

That last point is important too- it's easier to open something that used to be closed than it is to close something that used to be open. We spent a lot of time and effort on determining which APIs are exposed to Scoped Apps, and which ones aren't. We fully expect to continue adding to the APIs that are available over time. If we left out something near and dear to you, let us know.

 

Choosing the right model

Almost everything that you want to build can be done with Scoped Apps. However, sometimes it is more convenient or appropriate to use the legacy model. If you require access to an API that isn't available to Scoped Applications, then you may need to use the legacy model. If your processes are dependent on moving Update Sets around, the legacy model might be right for you. If you need to change the core functionality of a legacy application, then you need to do it in the legacy model.

 

When you are creating new functionality, or extending an existing application, then a Scoped App is probably the best choice.

 

Upcoming features

In Geneva, we're releasing the Studio. There's already been a couple of posts about it, and it's honestly the thing I am most excited about. I contributed heavily there, and I I hope we get a lot of feedback about to make it better in Helsinki and beyond. It also has an awesome Code Search tool which has made my life much easier these past several months. In Helsinki, we hope to have some more advanced collaborative development tools, as well as some advancements in the design-time and run-time permission model. Also, Code Search will be broken out into a separate, extendable Scoped App, accessible via a REST API. I wrote this, and I look forward to seeing what you do with it.

 

We aren't done iterating on the Scoped App model. As it gets wider adoption and the feedback comes pouring in, expect the experience to continually improve.

I have strange and mystical powers.

Adding a watermark in the subject takes an enviable position at the top of emails sent, backing its presence on the body and offering an alternative option to match with existing records.

 

I'll show you examples of:

  • Mail Scripts to implement the email subject changes
  • Appending the notification watermark into the subject
  • Why would you append the watermark in the subject

 

Watermarks are relevant when working with incoming emails classified as reply by an algorithm. By default, the system generates a watermark label at the bottom of each notification email to allow matching incoming email to existing records. The watermark always includes "Ref:" and a customizable prefix (default MSG), followed by the auto-numbered identifier of the source record (such as incident, problem, or change request). For example, Ref:MSG3846157

 

You have the option on the email notifications to add or omit the watermarks. However, you do not have the choice to add it to the subject. That is the reason we will need to implement a mail script to add the watermark into the subject.

 

email-validation-terms-21a.png

 

Mail Scripts to implement the subject changes

Mail scripts allow for business rule-like scripting within an outbound email message. With mail scripts, you can dynamically change the email output of your system based on different criteria. Mail scripts allow you to perform simple tasks, such as displaying incident data, and complex ones, such as making advanced database queries.

 

Create mail scripts in System Policy > Email > Notification Email Script

To make them run, then add a ${mail_script:script name} embedded script tag to the body of the email notification or template,replacing script name with the name of the script you created. This makes it easy to use the same scripts in multiple email notifications or templates.

 

We will be using the email properties to modify the subject of the notification.

 

Appending the notification watermark into the subject

To append the watermark into the subject of the notification, I created a new mail script.

Name = subject_watermark

addwatermarktosubject();
// This function will attach the watermark at the end of the subject
function addwatermarktosubject() {
    var vsubject = [email.getSubject(), email.watermark ].join(' ');
    email.setSubject(vsubject);
}

 

mail script.jpg

Then I created a notification with the following qualities:

 

Notification

 

 

Name

=

test_subject

Table

 

incident

Inserted

 

CHECKED

Updated

 

CHECKED

Users

 

ae@example.com

Subject

 

Any Subject

Message

 

Test

${mail_script:subject_watermark}

 

test email.jpg

 

When I make a change on the incident table, the result is a notification is triggered and the email subject has the watermark appended as intended.

append watermark email.jpg

 

 

Why would you append the watermark in the subject

There are several reasons you would like to make the watermark part of the subject. First, the system will first search the watermark on the subject. Second, watermarks on the body can be deleted without noticing them. Third, it makes watermark importance more evident for the customer. And finally, the text "Ref:" can be found on the body without being a real watermark.

 

There are a few cons, or negatives, about including a watermark in the email subject. Watermarks can not be hidden in the HTML body. It is also aesthetically not as pleasing as having the subject alone, without the watermark. It also makes the subject longer, which can be a turn off.

 

Adding the watermark on the subject provides flexibility when working with reply emails. It just requires creating a mail script that appends the watermark to the subject.

 

 

I tested this on Fuji with Chrome as browser.

 

More information on email watermarks see:

It is a best practice to scrub your update sets prior to moving them to Test or Production.  ServiceNow recommends keeping the number of changes in an update set to below 100.  This is about the outside maximum I advise as well.  Beyond that it becomes difficult to validate that all of the changes present are really ones you want to have in the instances you will be promoting to.  However, the method I am describing here could be used on any size of Update Set. You could use the method of creating several small update sets and then merging them when you are done with your changes, and have vetted all the records.

 

The Method:

 

I found that rearranging the Customer Update list view in my Update Set form, and adding an out-of-the-box Comment field really helps in this process.

 

Steps:

 

  1. Open up any Update Set.
  2. Right-Click on the Customer Updates fields header to bring up the context menu.
  3. Click on List Layout.

 

 

  4. Add and remove the fields until you have the following:

 

Update Set

Type

Target Name

Table

Action

Comments

Updated

Updated by

 

 

  5. Click the Save button.  Your Customer Updates list view should look something like this:

 

 

Initially the comment will be blank.  Examine each change and determine if it is valid or not.  If it is then fill in the comment field.  I tend to group mine according to the general change they are related to.  This allows me to do a mass update of records from the list view to fill in several at a time.  Leave blank any that you don't want included.  When you have completed examining all the records in the list then you can filter for only the comments that are blank. 

 

Now the controversial part:  Do we delete those records that have blank comments or what?

 

I have a serious aversion to deletion.  Although it is tedious I go into every blank-comment record and change the update set field to Default.

 

 

This moves the blank-comment change record from your update set to the default update set.  If you find later that the change was important you can always move it back using the same method.

 

This walking down and validating each record is a great method of ensuring that your update set contains only those changes desired to be moved, and that nothing bad, ugly, or undesirable ends up in your other instances!  The comment need not be anything really earth-shattering; simply just an indicator as to what the change is associated with.

 

Even though this appears to be slog-work; don't blow it off!  This has saved me multiple times and the process should be one you adopt for your development process!

Of interest; the more I have done this process the faster it becomes.  I have gotten so that certain patterns grab my eye and make it easier to spot changes I don’t want shipped.

 

Some useful Update Set wiki references:

 

Getting Started with Update Sets

Using Update Sets

Update Set Best Practices

Transferring Update Sets

System Update Sets

 

Steven Bell

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

The Geneva release has a number of new features of interest to developers on the ServiceNow platform. In this series of blog posts, I will cover some of these topics in a little more depth. As always, please leave feedback about which topics you would like to see covered.

Screen Shot 2015-11-03 at 9.28.36.png

The first thing you will see as you develop applications is the new Studio environment (System Applications > Studio). From here you can manage your downloaded scoped applications, as well as all the applications you are developing on the instance.

 

Screen Shot 2015-11-03 at 9.29.46.png

If you edit an application such as the Marketing Events application from the training courses, you will be presented with the new IDE interface.

 

Screen Shot 2015-11-03 at 9.31.00.png

 

The IDE interface allows you to interact with your application more like you would with a desktop development tool. You can open any of your already created application files with a click, or create new ones with a keyboard shortcut (Cmd-Shift-o).  Each of these application files will open in a new tab in the IDE so multiple files can be edited simultaneously without leaving any form pages or creating new browser tabs. You don’t have to complete work in any given tab to open another one since each tab is an independent context. If you want changes to reflect in another one (such as changing a table in one tab and then editing that table’s form in an another) you will have to save the work in the tabs and reload the other forms.

 

Screen_Shot_2015-11-03_at_9_35_26-with-box.png

 

As you edit application files, you will see the close control change to a blue dot. This is the indicator of an IDE tab with unsaved changes. If you try to close one of those tabs, you’ll be presented with a confirmation dialog to make sure that is what you intend. This makes it more difficult to accidentally close a tab with unsaved work in it. The system will try to prevent you from doing that.

 

Screen Shot 2015-11-03 at 9.38.49.png

Screen Shot 2015-11-03 at 9.39.06.png

 

When you open the “Create New Application File” dialog, you can either navigate from the category hierarchy or use the filter bar to select the type of file you want to create.

 

Screen Shot 2015-11-03 at 9.46.30.png

 

Note that as soon as you leave the name field, you’ll see the tab title change to display the information you entered. The IDE environment is highly responsive and aims to feel less like a web application and more like a native client tool.

 

Screen Shot 2015-11-03 at 9.48.06.png

 

You can also use the Go To functionality to search across the names of all your application files. This search works across both the Name field, as well as the object names themselves as you can see in this example.

 

Screen Shot 2015-11-03 at 10.05.21.png

 

The Code Search functionality allows you to search for code snippets. You can limit it to code on a single table via the drop down or you can even expand it across all applications on your instance with the checkbox.

 

Screen Shot 2015-11-03 at 10.06.05.png

 

When the search is executed, the results will show you the number of application files that have a match to the search string. For each matched file, it will show the title of the object with the number of individual lines that match and then show the matches in context.

 

Screen Shot 2015-11-03 at 10.13.21.png

 

This is just a small portion of the changes to make the developer experience more efficient and pleasant. In the next blog post, we will examine some of the new features for authoring script code in Studio.

 

Other entries in this series:

What's New in Geneva for Developers?

 

What Developers Need to Know about Geneva: Part 2 - Script Editor

Dave Slusher | Developer Advocate | @DaveSlusherNow | Get started at https://developer.servicenow.com

At the top bar of the form, the email icon will open a convenient tool that allows you to enjoy the flexibility of a quick email and the ability to use templates. These templates are called quick messages and can cause problems on the email client itself.

 

The ServiceNow email client enables you to send email directly from any record (such as an incident, change request, problem, or user record). After you enable the email client for an application, users can see an email icon in the header bar for each application record. All you need to do is click the email icon to launch the email client as a pop-up window.

 

one_bad_apple_10533_2.jpg

If the window does not appear, verify that your browser is not blocking pop-ups or the quick messages content.

Using the email client quick messages

Can Emails or quick messages allow you to insert predefined text into the message body of the email client. Selecting a quick message fills the Message Text field with the body specified in the quick message.

 

The idea is simple. To define quick messages go to System Policy > Quick Messages. Those messages will be shown at the top of the email client. After you define one or more quick messages, the Quick Message selector appears in the email client. You can reference variables directly in quick messages. Use the following syntax:  ${variable_name}.

 

How to identify a problematic quick message

If the email client fails to load for a particular user, please validate the Canned messages for the user (Quick messages).

 

Usual errors on canned messages are:

  • Mail scripts

  • Very long text

  • Complex encoded text inside

  • Attachments

  • String fields that contain unencoded HTML special entities like less than "<" or more than ">".

 

The email client does not support mail scripts. For scripts on the email client, use the email client template instead.

 

If a String field (e.g. short_description) is used inside the quick messages (e.g. as ${short_description}), it should contain a valid HTML text with HTML entiries fully encoded.

e.g To display

Text <<this "case" is 'simple' Düsseldorf >>

the String field (e.g short_description) value needs to contain:

Text &lt;&lt;this &quot;case&quot; is &apos;simple&apos; D&uuml;sseldorf &gt;&gt;

Using HTML encoded characters, ensure the browser will display the correct message.

If you use the less than (<) or greater than (>) signs in the String field inserted in the template, the browser might mix them with tags, becoming invisible but still present.

 

Here is an example that shows an error inside the new canned message called “error-canned-message” with body.

test of a problematic canned message
${mailscript:problematic_error}

 

Example of quick messages not supporting mailscript:

email_client_01.jpg

If you add problematic text inside the quick message, the email client fails to load and only "Compose Email" is shown at the top.

Another symptom could be that the quick messages do not seem to apply on the body.

 

Example of Compose Email not loading:

email_client.jpg

Our email client is sensitive to errors from the "Quick messages." To resolve those problems review the text on the relevant quick messages or the values used on the ${variable} inside the quick messages are valid HTML characters.

 

I've tested these example on Fuji, user Chrome as my browser.

 

I particularly like quick messages in the email client because it reminds me of Facebook messages. How do you use quick messages?

 

More information is available here:

The Geneva release is here, and it includes a number of features of interest to developers. These will be explored in depth over time, but here is a high level overview of some of the key points to explore.  If there is any specific topic you would like to see covered in more depth, leave a comment on this post.

 

Studio

 

The environment you use to interact with scoped applications, either from the ServiceNow App Store or ones that you are developing, has been revamped from the ground up. Studio has an IDE like editor experience that lets you edit multiple application files simultaneously. You can have a table definition open in one tab, a business rule in another and a script include in a third. Navigating back and forth between forms is a thing of the past, you can have them all open simultaneously and each with their own context.

Screen_Shot_2015-11-09_at_14_09_42-box.png

 

Studio also includes several new searching tools. You can search by name and/or type across all the files in your application to find a specific one. You can also do a code search across all the files of your application that will search the content of the files. This will search across them all, regardless of file type, so that will solve the problem of looking for a bit of code but not remembering if it was in a business rule, a script include or a Workflow. Even better, with a checkbox the search can be expanded to all scoped applications on the instance.

Screen Shot 2015-11-09 at 14.12.12.png

 

The Studio also includes several kinds of protection to keep developers from losing work. If you accidentally try to close a tab with unsaved work, you will be presented with a confirmation dialog that asks if you are sure you want to close.

If you are editing application files and get interrupted by session timeout or internet outages, you will be able to auto-recover the lost work even if your browser page refreshed.

 

Script Editor

 

Any place in the UI where you can edit code, HTML or XML you are now presented with auto-complete information. When editing the script of a Business Rule, for example, after you have selected which table the rule is for any time you access “current” in the script editor, it can auto-complete all the functions and fields on that object. This prevents you from having to look up the exact field names of tables as you write the script, now they are all accessible on the objects as you type. This also goes for JSON objects and any other Javascript object for which the editor knows the definition. Typing “Ctrl-<space>” will open any auto-complete information that is available for that variable.

Screen Shot 2015-11-09 at 14.14.08.png

Instead of having to do an explicit syntax check with the button as you edit code, there is now a real time syntax checker. As you type, you’ll see a yellow or red dot to the left of the code, indicating if there is a warning or error associated with that line. Hovering over the dot will show you the text of the warning or error.

Screen Shot 2015-11-09 at 14.15.13.png

 

Scripted REST APIs

 

As of Geneva, it is now possible to create a fully custom REST API inside of your scoped application. Once you name your API, you are given a base path that represents access to that set of APIs. Within that, you can specify individual resources with any of the HTTP verbs (GET,POST, PUT, DELETE, and PATCH) and write a script for that resource. The script has access to both the request and the response objects, as well as any parameters you define in the URL itself. This allows you to script any arbitrarily complex functionality you like as part of these APIs. You can query multiple tables within your scoped application and take action or return data based on them.

Screen Shot 2015-11-09 at 14.17.32.png

 

This allows you to create logical level APIs for your scoped application that are different from the built in REST APIs. It also allows you to create more abstract APIs that don’t require any knowledge of the underlying implementation details. For example, you could have a an API like

 

PUT http://instance-bame/api/x_14806_marketing/marketing_app/updateAttendeeName/{id}

 

that will take in data and update the attendee accordingly. The user of the API doesn’t need to know where there is a first_name field, or a single name field. The script implementing the API can handle that. Similarly, you could create an API that matches an existing legacy API. If existing applications rely on certain actions being available under a base URL, you can create all those endpoints and then implement the behavior you desire.

 

Rest Attachment API

 

Geneva includes a REST API for handling attachments. You can create a new attachment with a POST request, delete one with a DELETE request or use GET requests to read them in several ways. You can GET the list of attachments, GET the metadata about a given attachment or the binary of the file itself.

Screen Shot 2015-11-11 at 10.40.27.png

 

 

CORS Support

 

CORS support is new in Geneva. This allows you to define a set of external domains that are allowed to access REST apis on a ServiceNow instance. If, for example, you wanted to allow for AJAX calls from you main portal to interact with the Attachment API previous discussed, you could set up rules to allow that traffic. In this way, both the instance knows that this external traffic is permissible and modern browsers also understand that. It prevents the browser from giving an error about accessing AJAX resources that don't originate from the same domain as the current website.

 

Screen Shot 2015-11-11 at 10.47.42.png

Add Related Lists to external tables

 

New functionality that was added in Geneva allows a developer of a scoped application to add related lists to other forms. Let’s say you created a custom application that had an Outage table, with a reference to Incident. Inside your application you could configure the Incident form to have a related list that showed any referenced Outage records. On any instance that installed your custom application, the Incident form would then automatically receive this related list. This is without any changes in the Global scope or configuration to the Incident form itself, this relationship is contained within the scope of your application.

Screen Shot 2015-11-09 at 14.22.37.png

Screen Shot 2015-11-09 at 14.25.16.png

Other Enhancements to Integration

 

Other functionality added to Geneva includes Export Sets. Once you establish a MID server as the Export Target, you can define recurring exports to that target. You can export only the deltas from previous exports, define filtering or format and otherwise control the output to that MID server.

 

Outbound REST messages have expanded OAuth 2.0 support. This includes support for authentication headers, including the OAuth 2.0 Authorization Code flow to obtain access and refresh tokens. OAuth 2.0 profiles can be used for authentication as well.

 

Other entries in this series:

 

What Developers Need to Know about Geneva: Part 1 - Studio

What Developers Need to Know about Geneva: Part 2 - Script Editor

Dave Slusher | Developer Advocate | @DaveSlusherNow | Get started at https://developer.servicenow.com

Continuing from my previous article: Mini-Lab: Adding CSS To an Email Notification - Part 1 I would like to show how we can move beyond the inline Style attribute formatting to a Style section with Class attributes.  This is a much better way to organize and simplify CSS in your HTML.  Remember not all email services support this method!  I present it here as another tool for your toolbox.

 

Prerequisites:

 

1. Work through the Part 1 article labs.

2. Same as in Part 1 article with one exception:

a. You will want to send emails to yourself (your own account).  This will need to be a Microsoft Mail account or something NOT Google Mail-ish.  Navigate to System Properties -> Email, and fill in the field labeled: "Send all mail to this test email address".  Only after you have this filled in should you check the field labeled: "Mail sending enabled".  This will send emails to the account you specify instead of trying to send them to the user's example.com account (which is bogus).  When you have done these two things you can click the Save button to save your work.

3. Microsoft Outlook (local or web) to make sure everything is working correctly.  I will be using my web account.  If you don't have an account check this out: Outlook.

 

 

Lab 1.1: Style Tag Section

 

This type of style allows you to clean up your HTML by creating aliases to represent groupings of CSS statements.  This is done through the class attribute which will be placed where you would normally put an inline style attribute.  We will be taking the Email Notification Template we developed in the original Email Notification Scripts article, and enhancing it with HTML and a CSS Style section.

 

1. Navigate to System Policy -> Email Template.  A list view of all Email Templates will be displayed.

 

2. Pick our email template named: incident.ess.resolve.css.

 

3. Copy out the Message content to your editor.  We will be re-building our HTML around this text.

 

4. Here is the original message:

 

 

Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident:
${mailto:mailto.unsatisfied}

Short description: ${short_description}
Priority: ${priority}
Category: ${category}
Comments:
Additional comments: ${comments}
<hr/>
Click here to view Incident: ${URI_REF}

 

 

5. Again, like last time, while still in your text editor we will next be adding the HTML to the message to make it look nicer.  We will add a table and do a double-column to arrange and organize things.

 

 

<table cellpadding="0" cellspacing="0">  
    <tr>  
        <td colspan="2">Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident: ${mailto:mailto.unsatisfied}</td>  
    </tr>  
    <tr>  
        <td>Short Description: </td>  
        <td>${short_description}</td>  
    </tr>  
    <tr>  
        <td>Priority: </td>  
        <td>${priority}</td>  
    </tr>  
    <tr>  
        <td>Category: </td>  
        <td>${category}</td>  
    </tr>  
    <tr>  
        <td>Additional comments: </td>  
        <td>${comments}</td>  
    </tr>  
    <tr>  
        <td colspan="2"><hr/></td>  
    </tr>  
    <tr>  
        <td>Click here to view Incident: </td>  
        <td>${URI_REF}</td>  
    </tr>  
</table> 

 

 

a. You can copy this code and jump to Try It, and replace everything between the body tags.  Then click the See Result button to verify that you have the formatting essentially correct.

 

6. I will be re-using my spiffy graphic, and colors from the last article.

7. Now let's add in the CSS!

a. Create a style section in front of our <table> tag.

 

<style type="text/css">
.tableFormatting {width:650px;border:1px solid LightBlue;padding-bottom:10px;}
.tableHeading {background-color:DarkSlateBlue;padding-left:5px;width:55%;color:White;font-weight:bold;font-size:13px;}
.labelText {padding:5px;width:30%;background:Gold;font-weight:bold;font-size:12px;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;}
.variableText {padding:5px;width:59%;font-size:12px;background:Khaki;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;}
.altLabelText {padding:5px;width:30%;font-weight:bold;background:Yellow;border:1px solid LightBlue;font-size:12px;font-family:Arial, Helvetica, sans-serif;}
.altVariableText {padding:5px;font-size:12px;width:59%;border:1px solid LightBlue;background:Orange;font-family:Arial, Helvetica, sans-serif;}
</style>

 

Each of the "dot" labels are the class definitions we will be using as aliases in the follow-on HTML.

 

b. Now add these into HTML.  Your final HTML should look something like this:

 

<style type="text/css">
.tableFormatting {width:650px;border:1px solid LightBlue;padding-bottom:10px;}
.tableHeading {background-color:DarkSlateBlue;padding-left:5px;width:55%;color:White;font-weight:bold;font-size:13px;}
.labelText {padding:5px;width:30%;background:Gold;font-weight:bold;font-size:12px;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;}
.variableText {padding:5px;width:59%;font-size:12px;background:Khaki;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;}
.altLabelText {padding:5px;width:30%;font-weight:bold;background:Yellow;border:1px solid LightBlue;font-size:12px;font-family:Arial, Helvetica, sans-serif;}
.altVariableText {padding:5px;font-size:12px;width:59%;border:1px solid LightBlue;background:Orange;font-family:Arial, Helvetica, sans-serif;}
</style>
<table cellpadding="0" cellspacing="0"  class="tableFormatting">
  <tr>
      <td class="tableHeading" colspan="2">
          <img src="Pizza_Palace_Header.png" height="52" width="650"/>
      </td>
  </tr>
  <tr>
      <td class="tableHeading" colspan="2">Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident: ${mailto:mailto.unsatisfied}</td>
  </tr>
  <tr>
      <td class="labelText">Short Description: </td>
      <td class="variableText" >${short_description}</td>
  </tr>
  <tr>
      <td class="altLabelText">Priority: </td>
      <td class="altVariableText">${priority}</td>
  </tr>
  <tr>
      <td class="labelText" >Category: </td>
      <td class="variableText">${category}</td>
  </tr>
  <tr>
      <td class="altLabelText">Additional comments: </td>
      <td class="altVariableText">${comments}</td>
  </tr>
  <tr>
      <td class="labelText" colspan="2"><hr/></td>
  </tr>
  <tr>
      <td class="altLabelText" >Click here to view Incident: </td>
      <td class="altVariableText">${URI_REF}</td>
  </tr>
</table>

 

 

This is a much better organized approach than inline style attributes!!!  I just wish I could us this in Google Mail!  I'm crying here! 

 

8. Test out your new formatting by dropping the HTML into Try It.  Your result should look something like this:

 

1.outlook_try it html.png

 

9. Now the final step is to linerize it.  Do this in your editor.  Again I am using Notepad++ here.

 

10. Copy the linerized HTML and replace the message field in your Notification Email Template.  Your Email Template should look something like this:

 

1.outlook_email template.png

 

11. Click the Update button to save your changes.

 

12. Navigate to System Policy -> Email -> Email Notifications.  The Email Notifications list view will be displayed.

 

13. From the List View click on the "all" as we want to see everything both active and inactive.

 

14. Also from the List View deactivate the Incident Resolved CSS Google template, and activate the Incident Resolved CSS template (if inactive).  You may need to personalize your List View to add the Active field to the view.

 

1outlook_email notificaiton list view.png

 

Now your all set to test!!!

 

 

Lab 1.2: Testing The Notification Script

 

So, just like we tested it all in the last article.  It is pretty much the same here.

 

1. Navigate to Incidents -> Open.  The Open Incidents list view will be displayed.  FYI, make sure that the user is activated and has an email address in their respective user record.

 

2. Click on an Incident whose status is not Resolved.  The Incident form will be displayed.  Note the Incident number.

 

3. Click on the Resolve Incident button at the top-right of the form.  A pop-up will be displayed stating that you have fill in a handful of required fields.  Do so, and then click on the Resolve Incident button again. This should mark the incident as resolved.

 

4. You should now have received an email notification in your Outlook account. 

 

1.outlook_outlook_com.png

Looks good! 

 

 

Lab 1.3: Outlook Security and Graphics

 

Finally I would like to address the Outlook security issue a twitch more.  Essentially if your organization has cranked down on security the basic graphic notation we have been using will break.  You will need to use the .iix reference.

 

1. Navigate to System UI -> Images.  The Images list view will be displayed.

 

2. Edit up your graphic that you created in the last lab.  I will be using my really nifty header graphic as an example. 

 

3. Right-click on the graphic, and copy url.  If you paste this in your text editor you will find it is a bit different than what you have been using.  It should look something like this:

 

1.outlook_iix copy from image.png

The address should look something like this:

 

https://dev000000.service-now.com/0726ee9a0f23c6002ae14e9ce1050e26.iix

 

 

4. Use this in place of the reference in the <img> tag.

 

5. Test this out in Try It.

 

1.outlook_tryit iix.png

 

And you are done!  This should work great in your secure Outlook environment.  Watch it as it seems to bust otherwise.    Test! Test! Test!

 

There you have it!  You now have a hatful of techniques you can use to make stunning, brilliant, vibrant, and eye-watering content in your ServiceNow outbound emails!

 

BTW, I found this article to be an interesting read: Using CSS in HTML Emails.

 

Steven Bell

 

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

The infamous "send to event creator" option that is sometimes overlooked, can cause a headache later on if not addressed. All **** breaks loose when users start to report missing notifications. In light of iris.geist's post on Troubleshooting email notifications - Send to the Event Creator, let's talk about email notifications and different way to test the "send to the event creator" option.

 

With email notifications we can send selected users emails about specific activities in the system, such as updates to incidents, change requests, etc. There are several settings that affect how notifications behave and in support we often find that email notifications often are the reasons for people's pre-mature hair loss. The "send to the event creator" option is on the advanced notification view. By selecting this option you can send a notification to the person who performed the action that started the notification process.don'tforget.png

 

When 'send to event creator' is OFF (unchecked), the person who performed the action that started the notification process will be removed from the list generated from the notification Users/groups in fields,Users, or Groups field.

 

 

Send to event creator

Reason to use

Problem

Off - Unchecked

Users avoid receiving a notification of some actions executed by themselves

Some email will not be sent, which may not be explain by final users,  and as a result, some of them would complain of not receiving the notifications

On - Checked (Recommended)

No change on recipient list. Email notifications remain consistent

Some users will receive notifications of the actions done by themselves

 

 

Here are a few examples of how setting the "Send to event creator" option OFF, or unchecked, affects your recipient list.

For the purpose of this example, I have created/updated two users. I gave alejandra.prenatt the "itil" role so she can update the incident.

 

UserID

Email

Role

aileen.mottern

aileen.mottern@example.com

-

alejandra.prenatt

alejandra1.prenatt@example.com

itil

 

 

I created a new notification on incident called "test_event_creator."

 

Name

=

test_event_creator

Table

=

incident

Insert

=

CHECKED

Update

=

CHECKED

Users

=

aileen.mottern

alejandra.prenatt

Send to event creator

=

UNCHECKED

Subject

=

test event creator

Fill the reset of the required fields

 

For the purpose of this example, I left the option to Send to the Event Creator unchecked.

send event creator off.jpg

As you can see "aileen.mottern" and "alejandra.prenatt" are the target hard-coded recipients. Common sense says both users should get the notification. But, wait.

 

Example #1: I logged in as "aileen.mottern" and create a new incident e.g. INC0010020

When the notification fired:

  • "aileen.mottern" was NOT in the recipient list.
  • "alejandra.prenatt" was is in the recipient list.

 

This is because "aileen.mottern" was the user creating the incident thus the event creator. As the notification 'Send to event creator' is unchecked, it will remove it from the recipient list. From the list of "aileen.mottern" and "alejandra.prenatt", the event creator is removed.

 

 

Reviewing on the sysevent table, you can see the User name for the event.

event creator.jpg

 

Example #2: I logged in as "alejandra.prenatt," opened the incident created e.g. INC0010020 and add a comment, then Saved.

When the notification fired:

  • "aileen.mottern" is in the recipient list.
  • "alejandra.prenatt" is NOT in the recipient list.

 

This is because "alejandra.prenatt" was the user making the change thus the event creator. As the notification 'Send to event creator' is unchecked, it will remove it from the recipient list. From the list of "aileen.mottern" and "alejandra.prenatt." the event creator is removed.

 

Reviewing on the sysevent table, you can see the User name for the event.

event creator updated.jpg

Example #3: I logged in as the Administrator, opened the incident created e.g. INC0010020 and add a comment, then Save.

When the notification fired:

"aileen.mottern" and "alejandra.prenatt" are in the recipient.

 

This is because "System Administrator" was the user making the change, thus the event creator. From the list of "aileen.mottern" and "alejandra.prenatt," nothing needs to be removed.

 

Below is the final result after the three tests:

testing event creator.jpg

Users that make changes will not receive notifications that have the event creator UNCHECKED. However, you will want to set 'Send to event creator' ON (checked) if you want consistent notifications, as some users will not receive those notification when this is OFF (unchecked).

 

Target recipients

Notification Event creator

Event creator value

Final recipients

A,B,C

UNCHECKED

A

B,C

A,B,C

UNCHECKED

B

A,C

A,B,C

CHECKED

A

A,B,C

A,B,C

CHECKED

B

A,B,C

 

 

More information can be found here:

 

 

NOTE: Tested in Fuji, using Chrome.

In my previous article I showed how to use a Notification Email Script.  In this article I will show a method for adding Style information to spice up the email presentation!

 

There are two flavors of CSS Styles that can be utilized:

 

1. Inline Styles.  These are for primitive email handlers like Google Mail.

2. Style Sections and Class definitions.  These are for the rest of the email handlers.

 

In this article we will be focusing on Inline Styles.

 

Prerequisites:

 

1. This article builds off of my previous article.  So we will be using the Notification Template and testing found here: Mini-Lab: Email Notification Scripts

2. Familiarity with email notifications, and templates.

3. Familiar with HTML notation.

4. Familiarity with CSS style notation.

5. A good editor that does XML formatting, and preferably does Linerizing of XML (one-lining).  This latter will be important as ServiceNow Notification Emails work best with Linerized HTML.  I will be using Notepad++ to do this, and I have developed a technique to quickly Linerize and reverse such formatting (my technique?  undo of course).

6. You will want to send emails to yourself (your own account).  Navigate to System Properties -> Email, and fill in the field labeled: "Send all mail to this test email address".  Only after you have this filled in should you check the field labeled: "Mail sending enabled".  This will send emails to the account you specify instead of trying to send them to the user's example.com account (which is bogus).  When you have done these two things you can click the Save button to save your work.

 

 

Lab 1.1: Inline Styles

 

This type of style is pretty straightforward.  For every HTML tag you have to indicate what style you want applied to that line or section.  So we will be taking the Email Notification Template we developed in the last article and enhancing it with HTML and inline CSS Styles.

 

1. Navigate to System Policy -> Email Template.  A list view of all Email Templates will be displayed.

2. Pick our email template named: incident.ess.resolve.css.

3. Copy out the Message content to your editor.  We will be building our HTML around this text.

4. Here is the original message (kind of bland isn't it?):

 

Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident:
${mailto:mailto.unsatisfied}

Short description: ${short_description}
Priority: ${priority}
Category: ${category}
Comments:
Additional comments: ${comments}
<hr/>
Click here to view Incident: ${URI_REF}

 

5. Still in your text editor we will next be adding the HTML to the message to make it look nicer.  We will add a table and do a double-column to arrange and organize things.

 

<table cellpadding="0" cellspacing="0">
    <tr>
        <td colspan="2">Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident: ${mailto:mailto.unsatisfied}</td>
    </tr>
    <tr>
        <td>Short Description: </td>
        <td>${short_description}</td>
    </tr>
    <tr>
        <td>Priority: </td>
        <td>${priority}</td>
    </tr>
    <tr>
        <td>Category: </td>
        <td>${category}</td>
    </tr>
    <tr>
        <td>Additional comments: </td>
        <td>${comments}</td>
    </tr>
    <tr>
        <td colspan="2"><hr/></td>
    </tr>
    <tr>
        <td>Click here to view Incident: </td>
        <td>${URI_REF}</td>
    </tr>
</table>

 

    a. Copy this code.  Jump to Try HTML, and replace everything between the body tags.  Then click the See Result button.

 

2.try it out_1.png

 

That helped!  You can see it organized the original data nicely.  However, we can do better than that. 

 

5. I decided to add a cool graphic to my email.  I took a photo I had, edited it in Microsoft Paint, added some text, and saved it down.

   a. Navigate to System UI -> Images.  The Images list view will be displayed.

   b. Click the New button.

   c. Fill out the form with something like this:

Active: checked

Category: General

Name: Pizza_Palace_Header.png

Image: <<upload your image here>>

 

   d. Right-click on the form header and Save your work.

 

3.css_pizza_palace_image.png

 

NOTES:

 

After you add your image you will see a suggested link to reference your new image displayed on the form.  This works great for most email handlers.  It does NOT work for Microsoft Outlook.  Especially if your network has additional email security.  In this case you will need to use the .iix reference url.  To obtain this right-click on your graphic and copy the url.  This will be the .iix reference.

 

Copy down the height and width and url to use in your HTML.  In my case it was:  <img src="Pizza_palace_header.png" width="651" height="53" />

 

Interesting to note that this won't work in Try HTML, but it will work in your email; as the source is embedded in the email html!

 

6. This will work in both Google Mail and others as is.  However, some Microsoft Outlook implementations with security you will need to use the .iix reference.  Now let's add in some CSS style code!

 

I chose my colors from the following location: HTML Color Names

 

Inline styles are simply added to the lines that can support them such as "table" and "td" as you see here.  The following article does a decent job of explaining the HTML Style Attribute.

 

So I will add in some formatting, and fix the width, and mess with colors (don't laugh), and fonts, etc.

 

<table cellpadding="0" cellspacing="0" style="width:650px;border:1px solid DarkBlue;padding-bottom:10px;">
    <tr>
        <td colspan="2" style="background-color:DarkSlateBlue;padding-left:5px;width:55%;color:White;font-weight:bold;font-size:13px;">
            <img src="Pizza_Palace_Header.png" height="52" width="650"/>
        </td>
    </tr>
    <tr>
        <td colspan="2" style="background-color:DarkSlateBlue;padding-left:5px;width:55%;color:White;font-weight:bold;font-size:13px;">Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident: ${mailto:mailto.unsatisfied}</td>
    </tr>
    <tr>
        <td style="padding:5px;width:30%;background:Gold;font-weight:bold;font-size:12px;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;">Short Description: </td>
        <td style="padding:5px;width:59%;font-size:12px;background:Khaki;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;" >${short_description}</td>
    </tr>
    <tr>
        <td style="padding:5px;width:30%;font-weight:bold;background:Yellow;border:1px solid LightBlue;font-size:12px;font-family:Arial, Helvetica, sans-serif;">Priority: </td>
        <td style="padding:5px;font-size:12px;width:59%;border:1px solid LightBlue;background:Orange;font-family:Arial, Helvetica, sans-serif;">${priority}</td>
    </tr>
    <tr>
        <td style="padding:5px;width:30%;background:Gold;font-weight:bold;font-size:12px;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;" >Category: </td>
        <td style="padding:5px;width:59%;font-size:12px;background:Khaki;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;">${category}</td>
    </tr>
    <tr>
        <td style="padding:5px;width:30%;font-weight:bold;background:Yellow;border:1px solid LightBlue;font-size:12px;font-family:Arial, Helvetica, sans-serif;">Additional comments: </td>
        <td style="padding:5px;font-size:12px;width:59%;border:1px solid LightBlue;background:Orange;font-family:Arial, Helvetica, sans-serif;">${comments}</td>
    </tr>
    <tr>
        <td style="padding:5px;width:30%;background:Gold;font-weight:bold;font-size:12px;border:1px solid LightBlue;font-family:Arial, Helvetica, sans-serif;" colspan="2"><hr/></td>
    </tr>
    <tr>
        <td style="padding:5px;width:30%;font-weight:bold;background:Yellow;border:1px solid LightBlue;font-size:12px;font-family:Arial, Helvetica, sans-serif;" >Click here to view Incident: </td>
        <td style="padding:5px;font-size:12px;width:59%;border:1px solid LightBlue;background:Orange;font-family:Arial, Helvetica, sans-serif;">${URI_REF}</td>
    </tr>
</table>

Yuck on the code.  Oh well.  Messy though the html may be; it is what works in Google Mail.  :-p

 

Again copy the code and let's test it with Try HTML.

 

3.try it out_2.png

 

7. Now the final step is to linerize it.  Do this in your editor.  Again I am using Notepad++ here.

 

1.css_linarize the xml google.png

NOTE: To revert the linerization I simply "undo" the last edit in Notepad++, and it returns my formatting.

 

4.linerized code.png

 

8. Copy the linerized HTML and replace the message field in your Notification Email Template.

 

9. Change the name to: incident.ess.resolve.css.google

 

2.css_linerized email template google.png

 

10. Click the Update button to save your changes.

 

11. Navigate to System Policy -> Email -> Email Notifications.  The Email Notifications list view will be displayed.

 

12. Search for notification named "Incident Resolved CSS".  Edit this notification (we created this in the last article).

 

13. Change the Active field to un-checked, right-click the form header to bring up the context menu, and click Save.  This deactivates the Notification we created in the last lab.

 

14. Change the following on the still open form:

 

a. Name: Incident Resolved CSS Google

b. Active: Checked.

c. What it will Contain -> Email Template: incident.ess.resolve.css.google

 

NOTE: This is where we reference our new Email Notification Template.  Also, I named it google because inline styles are the only thing that Google Mail understands.

 

d. Right click on the form header and choose Insert And Stay so that we do not save over our old Notification!

 

15. If you have not already done this in the previous article then right click on the form header and click on Configure -> Form Layout.  The Form Layout form will be displayed.

 

a. Add the following fields:

 

Send Event to Creator

Force delivery

 

b. Click the Save button.  This will return you to the Email Notification form.

 

NOTE: Send Event to Creator makes sure an email is sent (it should be checked automatically).  Force delivery makes sure an email is sent even if the user has notification disabled (I just wanted to make sure the email is sent - a bit of overkill since in our prerequisites the users we will be using for testing should have Notification enabled).

 

16. Make sure both new fields are checked.  Click the Submit button to save your work.

 

 

Lab 1.2: Testing The Notification Script

 

So, just like we tested it all in the last article.  It is pretty much the same here.

 

1. Navigate to Incidents -> Open.  The Open Incidents list view will be displayed.  FYI, make sure that the user is activated and has an email address in their respective user record.

 

2. Click on an Incident whose status is not Resolved.  The Incident form will be displayed.  Note the Incident number.

 

3. Click on the Resolve Incident button at the top-right of the form.  A pop-up will be displayed stating that you have fill in a handful of required fields.  Do so, and then click on the Resolve Incident button again. This should mark the incident as resolved.

 

4. Navigate to System Policy -> Events -> Event log.  The Event log list view will be displayed.

 

5. Filter for the Name "incident.updated".  You should see that this event has recently (within a couple of seconds) fired.

 

 

6. Navigate to System Logs -> Emails.  This will display the Email Log list view.

 

7. Filter for Subject contains "has been resolved".  You should see the email notification sent out for your incident number.  Edit this notification up.

 

8. Examine the notification body and verify that there the text contains something like:

 

"Your incident INC0010051 has been resolved and will automatically close in 24 hours. If you feel the issue is not resolved, please click the following link to reopen your incident:"

 

9. Now, I should have received an email in my Google account (this could be different for you all of course).

 

5.Google Email.png

 

Okay, okay, don't laugh, I am trying to make a point here!

 

Anyway, you get the idea.  It is possible to do some pretty amazing things using this technique with the outbound emails.  In the next article I will be showing how to set this up an alternative way:  Through a Style section and Class tags; rather than inline.

 

Steven Bell

 

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

I thought I would use this and the next article to tackle advanced email notifications.  In this article I will be modifying an existing notification template to use an email notification script.  Notifications scripts are used to dynamically modify the content of an email, and can be really useful in listing additional information pertaining to the record that triggered the notification.

 

Prerequisites:

 

1. Familiarity with how to edit forms to add fields not currently showing.

2. Familiarity with email notifications, how to add them, how to modify them.

 

For those of you using a personal instance you will have to do a couple of things prior to being able to get emails to fire.

 

1. Make sure that the users that you will be working with have Notification set to Enable. 

2. Make sure that the Admin account has Notification set to Enable.

3. If you want to send emails to yourself (your own account) then navigate to System Properties -> Email, and fill in the field labeled: "Send all mail to this test email address".  Only after you have this filled in should you check the field labeled: "Mail sending enabled".  This will send emails to the account you specify instead of trying to send them to the user's example.com account (which is bogus).  When you have done these two things you can click the Save button to save your work.

 

Quick diagram of Notification dependencies

 

 

You don't need a template or a script, but they help in code reuse (a best practice).

 

 

Lab 1.1: Email Notification Script

 

We will be modifying an existing Email Notification that contains a Mail Script tag and moving that code down a level to be a Notification Script that will be included in the notification.  We will be saving all of this down as our own and deactivating the original out-of-the-box version (another best practice).

 

1. Navigate to System Policy -> Email -> Email Templates.  The Email Templates List View will be displayed.

 

2. Search for the template named ess.incident.resolve, and edit that template up.

 

3. Change the following:

 

  a. Name: incident.ess.resolve.css

 

NOTE: We are using css because we will be expanding on this example to pretty it up in the next article.

   

  b. Message:

 

Your incident ${number} has been resolved and will automatically close ${mail_script:autoCloseStatement}. If you feel the issue is not resolved, please click the following link to reopen your incident:

${mailto:mailto.unsatisfied}

 

Short description: ${short_description}

Priority: ${priority}

Category: ${category}

Comments:

Additional comments: ${comments}

<hr/>

Click here to view Incident: ${URI_REF}

 

  c. IMPORTANT:  Right-click on the form header and pick Insert.  Do NOT update or save as this will update the out-of-the-box version.

 

NOTE: All we did here was rip out everything (including the tags) that were between the <mail_script> tags and replace it all with a call to our new Notification Script, um, which we haven't written yet.  That's next!

 

 

4. Navigate to System Policy -> Email -> Notification Email Scripts.  The Nofication Email Scripts list view will be displayed.

 

5. Click the New button.  This will display an empty Email Script form.

 

6. Fill in the form with the following:

 

a. Name: autoCloseStatement

b. Script:

 

  var days = parseInt(gs.getProperty("glide.ui.autoclose.time"));
  var hours = days * 24;

  if (days == 0)
      template.print('now');
  else if (days <= 3)
      template.print('in ' + hours + ' hour' + ((hours>1) ? 's' : ''));
  else
      template.print('in ' + days + ' day' + ((days>1) ? 's' : ''));

 

c. Click the Submit button to save your work.

 

Did the script look familiar?  It was everything we removed from the Email Notification Template that was between the <mail_script> tags.  Easy!

 

 

7. Navigate to System Policy -> Email -> Email Notifications.  The Email Notifications list view will be displayed.

 

8. Search for notification named "Incident Resolved".  Edit this notification.

 

9. Change the Active field to un-checked, right-click the form header to bring up the context menu, and click Save.  This deactivates the out-of-the-box Notification.

 

10. Change the following on the still open form:

 

a. Name: Incident Resolved CSS

b. Active: Checked.

c. What it will Contain -> Email Template: incident.ess.resolve.css

 

NOTE: This is where we reference our new Email Notification Template.

 

d. IMPORTANT: Right click on the form header and choose Insert And Stay so that we do not save over the out-of-the-box Notification!

 

11. Right click on the form header and click on Configure -> Form Layout.  The Form Layout form will be displayed.

 

a. Add the following fields:

 

Send Event to Creator

Force delivery

 

b. Click the Save button.  This will return you to the Email Notification form.

 

NOTE: Send Event to Creator makes sure an email is sent (it should be checked automatically).  Force delivery makes sure an email is sent even if the user has notification disabled (I just wanted to make sure the email is sent - a bit of overkill since in our prerequisites the users we will be using for testing should have Notification enabled).

 

12. Make sure both new fields are checked.  Click the Submit button to save your work.

 

1.email notification.png

 

Okay, that is it!  We are ready to test our creation!

 

 

Lab 1.2: Testing The Notification Script

 

1. Navigate to Incidents -> Open.  The Open Incidents list view will be displayed.

 

2. Click on an Incident whose status is not Resolved.  The Incident form will be displayed.  Note the Incident number.

 

3. Click on the Resolve Incident button at the top-right of the form.  A pop-up will be displayed stating that you have fill in a handful of required fields.  Do so, and then click on the Resolve Incident button again. This should mark the incident as resolved.

 

4. Navigate to System Policy -> Events -> Event log.  The Event log list view will be displayed.

 

5. Filter for the Name "incident.updated".  You should see that this event has recently (a within a couple of seconds) fired.  I did it a few times while I was testing the code for this article.  :-)

 

 

6. Navigate to System Logs -> Emails.  This will display the Email Log list view.

 

7. Filter for Subject contains "has been resolved".  You should see the email notification sent out for your incident number.  Edit this notification up.

 

8. Examine the notification body and verify that there the text contains something like:

 

"Your incident INC0010051 has been resolved and will automatically close in 24 hours. If you feel the issue is not resolved, please click the following link to reopen your incident:"

 

 

That's it!  If you see that type of message then it worked!  The "24 hours" is generated by the script.  So let's spiff it up a bit.

 

 

Lab 1.3: Email Notification Script Best Practice

 

Here we will up the game a bit by making the script more extensible, and maintainable.  We will also be introducing a new twist on a switch statement, and implementing the script as a generic function to isolate it from accidental execution.

 

1. Navigate to System Policy -> Notification Email Scripts.  The Email Notification Scripts list view will be displayed.

 

2. Filter for the script named "autoCloseStatement", and edit up the script.

 

3. Change the script to the following:

 

(function() {
    var days = parseInt(gs.getProperty("glide.ui.autoclose.time"));
    var hours = days * 24;

    var autoCloseStatement = '';
    switch (true) {
        case (days == 0):
            autoCloseStatement = 'now';
            break;
        case (days <= 3):
            autoCloseStatement = 'in ' + hours + ' hour' + ((hours>1) ? 's' : '');
            break;
        default:
            autoCloseStatement = 'in ' + days + ' day' + ((days>1) ? 's' : '');
            break;
    }

    template.print(autoCloseStatement);
})();

 

 

NOTES:

 

The editor throws a warning on the switch statement.  "The 'switch' should be an 'if'".  Really?  Seriously, ignore this warning as it is simply nuts. 

 

The template.print places the generated string back into the email template text at the location of the script call.

 

Here we have two best practices:

1) Anonymous function - this keeps the code from being accidentally executed.  Only this Notification Script can execute this code.

2) Whenever an "if" statement goes beyond one if/then/else you should look at converting it to a "switch" statement.  This improves readability, flexibility, and extensibility.

 

Twist: Note the unusual switch statement.  This has a "true" in the argument!  This is how you can convert mixed "if" statement code into a "switch".  You do so by making the passed value "true", and then each argument of the if/else if/etc. can be included in parenthesis in each case statement.  Pretty cool, huh?  :-)

 

4. Click the Submit button to save your work.

 

 

5. Now repeat lab 1.2 to test.

 

There you go!  A definite upgrade from out-of-the-box!  :-)

 

In the next article we will be exploring CSS in email notifications.

 

Steven Bell

 

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

In one of my responses out on the The specified item was not found. forum kevinanderson made a wistful comment about wishing we had a compatible port of underscore.js.  It got me to thinking, and I believe I have found a pretty straightforward way to implement it!

 

Prerequisites:

 

1. Know the basics of scripting in ServiceNow.

2. Familiar with UI Action, Fix Scripts, Script Includes, and UI Scripts.

 

What will be covered:

 

1. Script Include port

2. UI Script port

3. GlideSystem Include (gs.include)

4. Testing of the entire port

 

Requirements:

 

1. The underscore.js library must be available and fully functional on both the front- (browser), and back-end (Server) Scripting environments.

 

Design:

 

1. Since UI Scripts are not available to the Server, and Script Includes are not available to the browser this will have to be a split or double port.

 

 

Lab 1.1: Port underscore.js to the Server Side

 

1. In your ServiceNow instance navigate to System Definition -> Script Includes.  The Script Includes list view will be displayed.

 

2. Click on the New button.  A blank Script Include form will be displayed.

 

3. Fill in the form with the following:

 

Name:  underscorejs.min

Client Callable: unchecked

Active: checked

Leave the Script blank for the moment.

 

4. Now we need the actual underscore.js code.  There are two flavors:  full, and min.  Min means minified and comes out to around 10k-ish bytes size.  Perfect for our needs so we will be using that.  The full code is worthy of study.

 

a. Navigate to Underscore.js homesite -> GitHub -> underscore-min.js

b. Copy out just code from the window.

 

1.underscore_github copy.jpgc. Go back to your Script Include and paste the underscore.js code into the Script field.

 

2.underscore_script include.png

NOTE: That the editor complains (the red "x") next to line 5.  If you hover over that icon it should say:  Expected ')' to match '(' from line 5 and instead saw ','.  Ignore this error.

 

d. Click the Submit button to save your work.  You will get an error popup.  Click the ok button and save the script (it might actually pop more than once so keep clicking Ok).

 

3.underscore_popup error.pnge. That is it for activating the underscore.js library on the Server side.  So let's test it out.

 

5. Navigate to System Definition -> Fix Scripts.  The Fix Scripts list view will appear.

 

6. Click the New button.  A blank Fix Scripts form will be displayed.

 

7. Fill the form out with the following:

 

Name: Underscore.js Test

Active: checked

Description: Code to test the underscore.js library (Server Side)

Script:

 

gs.include('underscorejs.min');  // bring in our library

var even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
gs.print(even);

var map1 = _.map([1, 2, 3], function(num){ return num * 3; });
var map2 = _.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
var map3 = _.map([[1, 2], [3, 4]], _.first);

gs.print(map1);
gs.print(map2);
gs.print(map3);

 

NOTE:  I copied and modified the examples from the underscore.js home page. 

 

8. Click on the Submit button to save your work.

 

4.underscore_fix script.png

 

9. Now run your Fix Script.  You should see the following results:

 

5.underscore_fix script_result.png

Woohoo!  It worked!  Now let's go get it working for the browser side.

 

 

Lab 1.2: Port underscore.js to the Browser Side

 

Since this type of Script Include is not available to the Client Side then we need to create a library that is also available to the Browser.  To do this we will need to use a UI Script.

 

1. From your ServiceNow instance navigate to System UI -> UI Scripts.  The UI Scripts list view will be displayed.

2. Click on the New button.  A blank UI Script form will be displayed.

3. Fill out the form with the following:

 

a. Name: underscorejs.min

b. Global: checked

c. Active: checked

d. Script:  Go get the underscore-min.js code again and paste it here just like you did with the Script Include in Lab 1.1.

e. Click the Submit button to save your work.  You will get the same Syntax Error popups that you received with the Script Include submit.  Click Ok on all that appear.  They have no affect on things.  Just the editor complaining about nothing. 

 

6.underscore_ui script.png

 

NOTE: Global UI Scripts are not available in Scoped applications.

 

Now we are done with setup, let us go and do a test!  For that we will be creating a new button on the Incident form that will do nothing more than test our new underscore.js library.  This button will not interact with the Incident, but instead the Incident provides a platform for us to test our functionality.

 

4. Navigate to System UI -> UI Actions.  The UI Actions list view will be displayed.

 

5. Click the New button.  A blank UI Action form will be displayed.

 

6. Fill out the form with the following:

 

a. Name: Test Underscore.js

b. Table: Incident

c. Order: 100

d. Form Button: checked

e. Active: Checked

f. Show insert: Checked

g. Show update: Checked

h. Client: Checked

i. Onclick: test()

j. Script:

 

function test() {
    alert ('ready');
    try {
        _.each([1, 2, 3], alert);
    }
    catch(err) {
        alert(err);
    }
}

 

7. Click the Submit button to save your work.

 

7.underscore_ui action.png

 

8. Navigate to Incident -> Open.  The Open Incidents list view will be displayed.

 

9. Click on an Incident and open it.  The Incident form should be displayed and you should see our new button in the upper right of the form.

 

8.underscore_incident test.png

10. Click on the Test Underscore.js button.  You should see an Alert box like this:

 

9.underscore_ready.png

 

11. Keep clicking Ok and you will see the following popups in a row:

 

10.underscore_one.png

11.underscore_two.png

12.underscore_three.png

 

It works!!!! Woohoo!  Cool stuff!

 

12. Now go and mark your UI Action active field as unchecked to turn it off.

 

You now have a front- and back-end implementation of underscore.js.  Obviously this opens up the ability to bring in other libraries (perhaps Node.js?).

 

So there you go Kevin; challenge met!

 

Steven Bell.

 

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

The other day I wanted to detect exactly one type of error.  The question was how do you figure out and more importantly react to certain error types.  First I needed to know the current definition of the Error object.  So back to my old friend MDN to find that out.

 

MDN Error Definition

 

I scrolled down to the ErrorTypes section.  Here is the short list:

 

EvalError - an error that occurs regarding the global function eval().

InternalError - an error that occurs when an internal error in the JavaScript engine is thrown. E.g. "too much recursion".

RangeError - error that occurs when a numeric variable or parameter is outside of its valid range.

ReferenceError - error that occurs when de-referencing an invalid reference.

SyntaxError - a syntax error that occurs while parsing code in eval().

TypeError - an error that occurs when a variable or parameter is not of a valid type.

URIError - an error that occurs when encodeURI() or decodeURI() are passed invalid parameters.

 

So, theoretically we should be able to catch these kinds of errors.  Actually the only one I have seen consistently is SyntaxError.  I would have to play around to see if I could get the others to trigger and it may be that these are ECMAScript 3>.  If so, they may not even be present yet in ServiceNow JavaScript.

 

BTW, I have also seen this error in the past so we should be able to do a check for it:

 

com.glideapp.workflow.model.ModelRunTimeException

 

Armed with all of this I flew out to my handy-dandy personal instance.  Quickly I navigated to and create a new Fix Script!  Feverishly I typed out the following:

 

Name: JavaScript Error Object Test

Description:

Quick script from:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError

Script:

 

// Copy and paste these from the above link, and change console.write to gs.print
try {
  eval('hoo bar');
} catch (e) {
  gs.print(e instanceof SyntaxError); // true
  gs.print(e.message);   // "missing ; before statement"
  gs.print(e.name);   // "SyntaxError"
  gs.print(e.fileName);   // "Scratchpad/1"
  gs.print(e.lineNumber);   // 1
  gs.print(e.columnNumber);   // 4
  gs.print(e.stack);   // "@Scratchpad/1:2:3\n"
}

try {
  throw new SyntaxError('Hello', 'someFile.js', 10);
} catch (e) {
  gs.print(e instanceof SyntaxError); // true
  gs.print(e.message);   // "Hello"
  gs.print(e.name);   // "SyntaxError"
  gs.print(e.fileName);   // "someFile.js"
  gs.print(e.lineNumber);   // 10
  gs.print(e.columnNumber);   // 0
  gs.print(e.stack);   // "@Scratchpad/2:11:9\n"
}

 

I clicked the submit button to save my work.

 

I then ran the Fix Script.  Here were my results:

 

 

Imagine my surprise when I found that the instanceof worked!  Wow!  No kidding.  I seriously thought it would break.  Hmmm, maybe I had it spelled wrong on my previous article...doh!

 

Ok, so what failed?

 

filename, column number, and (crying here) stack.  On the second try, catch the line-number failed.  I went back out to the MDN site and lo!  If you read the fine-print you find that these four have not yet been standardized!  Ah!  Well then, I am happy for what does work!

 

With what worked I can now use the "instanceof" on an error in the catch and actually look at types of errors (a really good explanation of how to use instanceof can be found here you will have to scroll down to the bottom of the article).  That failing I can at least look at the name and snag that.

 

BTW, since we are throwing an error the syntax to throw will have to be looked at because it obviously does not work this way in ServiceNow.  More on throwing errors here.  You will have to play around with that and see how it works (my challenge to you!  Here is a good start: errors). 

 

Steven Bell.

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

There is something I have observed as a performance issue: Hung workflow jobs.  A lot of administrators simply don't know that there may be literally thousands of these sitting there in the Active Contexts list.  Or just a few.  It depends on the age of your company's instance.  A worker process has to be assigned every few seconds to go spin through all of these and see if there has been a state change.  If so, then the worker handles the change.  If not, it keeps looking until it reaches the end, and goes off to do other work.  Enough of these and that worker is tied down for the duration.

 

Therefore I have wanted a way to kill hung (executing) contexts en-mass for some time.  This feature is not available in ServiceNow out-of-the-box (OOtB).  However, I have found code on the web that allows you to blow everything out of the Executing Workflows list (not my desired functionality).

 

Now I have been mentioning this for awhile in my ServiceNow Scripting classes (trying to get someone else to write it! ).  One class recently flat out challenged me to write one and publish it here.  I finally broke down and took the challenge!  Here you are!

 

I started by investigating how the Cancel Workflow UI Action from the executing Workflow Context works.  It uses GlideAjax and a couple of Glide dialogs to manage the cancelling of an active workflow.  The GlideAjax function calls an Ajax Script Include (WorkflowCancelKill) on the server-side.  This script is passed the current workflow context sys_id and attempts to cancel the job.  If it fails, it then prompts the user if they want to attempt to kill the context.  If so, then another call is made to the Ajax Script Include to fire a broadcastKill call and try to take it out that way.  If that fails then, well, you manually need to go delete that job.

 

So, borrowing heavily from the OOtB Cancel I used essentially the same functionality, but adapted it to a List View UI Action.

 

Pre-requisites:

 

A working knowledge of Scripting in ServiceNow (class)

A working knowledge of Workflows, and the Workflow Editor in ServiceNow (class)

 

Scripting Features Utilized:

 

Client Script

g_list object

GlideDialogWindow

UI Action

     this.bind

     callback function

     List Choice functionality from a UI Action

GlideAjax

 

Script Include

Workflow object

cancelContext

broadcastKill

 

Lab 1.1: Creating the Ajax Script Include

 

First we will need to create our script include that carries out our Cancel and Execute commands.

 

1. From your ServiceNow instance navigate to System Definition -> Script Includes.  The list view of Script Includes will be displayed.

2. Click on the New button.  The new Script Include form will be displayed.

3. Fill in the form with the following:

     a. Name: CancelWorkflowContextsAjax

     b. Accessible From: All application scopes

     c. Client Callable: Checked

     d. Active: Checked

     e. Description: Kill context from the executing workflows list view

     f. Script:

 

var CancelWorkflowContextsAjax = Class.create();
CancelWorkflowContextsAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    cancelContexts: function() {
        var cancelIds = this.getParameter('sysparm_contextIds').split(',');

        // loop through all of our workflow context sys_ids
        for (var i=0; i < cancelIds.length; i++) {
            this.cancelSingleContext(cancelIds[i]);
        }

        gs.info('[{0}]:\n {1}', this.type, this.message);
    },

    // Cancel OR Kill a single workflow context
    cancelSingleContext: function(contextId) {
          try {
                this.message += '\n---> Attempting to cancel: ' + contextId + '\n';
                var wfContext = new GlideRecord('wf_context');
                wfContext.get(contextId);
                new Workflow().cancelContext(wfContext);
                this.message += '---> Workflow canceled: ' + contextId + '\n';
          }
          catch(err) {
                this.message += '---> ERROR: ' + err + '\n';

                // If we fail and it was due to a ModelRunTimeException, then attempt a kill
                // Interestingly since we do not have "instanceOf" available in the language
                // definition we have to play the game of searching for it in the message itself
                // (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof)
                //com.glideapp.workflow.model.ModelRunTimeException
                if (err.toString().indexOf('ModelRunTimeException') > -1) {
                    this.killContext(contextId);
                }
          }
    },


    // This fires a kill message to the specified workflow context
    killContext: function(contextId) {
        this.message += '---> Attempting to kill context: ' + contextId + '\n';
        var wfContext = new GlideRecord('wf_context');
        wfContext.get(contextId);

        var workFlow = new Workflow();
        workFlow.cancel(wfContext);  // first cancel the context
        workFlow.broadcastKill(wfContext); // now send the kill message to the context

        this.message += '---> Workflow context killed: ' + contextId + '\n';
    },

    type: "CancelWorkflowContextsAjax",
    message: ""  /* this is our general message container */
});

 

     g. Click on the Submit button to save your work.

 

1. Script Include.png

 

     We are now ready to build the browser-side code.

 

 

Lab 1.2: Creating the Ajax UI Action

 

Now we will create our List View List Choice UI Action to carry out actions against a list of chosen executing Workflows

 

1. From your ServiceNow instance navigate to: System UI -> UI Actions.  This will display the List View of all UI Actions.

2. Click on the New button.  This will bring up a blank UI Action form.

3. Fill in the form with the following:

a. Name: Cancel Workflows

b. Table: Workflow Context [wf_context]

c. Order: 250

d. Action Name: blank

e. Active: checked

f. Show insert: checked

g. Show update: checked

h. Client: checked

i. List choice: checked

     All other check boxes: unchecked

j. Comments: Cancel the selected list of workflows.  If cancel fails attempt to automatically force the kill.

k. Hint: Cancel the selected list of workflows

l. Onclick: cancelWorkflowContexts();

m. Condition: gs.hasRole('admin')

n. Script:

 

function cancelWorkflowContexts() {
    var contextIds = g_list.getChecked().split(',');
   
    if (!contextIds || contextIds.length == 0) {
        return;
    }

    // our callback function from the dialog that executes the ajax call
    var callback = function() {
         var cancelContexts = new GlideAjax("CancelWorkflowContextsAjax");
         cancelContexts.addParam("sysparm_name", "cancelContexts");
         cancelContexts.addParam("sysparm_contextIds", contextIds.join(','));

         // Our Async callback handler
         var handleResponse = function () {
             reloadWindow(window);  // reload the list view
         };

         // We really don't care about the answer, and throw it away
         // Our callback here is to fire the reloadWindow
         cancelContexts.getXMLAnswer(handleResponse.bind(this));
         return true;
    };

    // Glide dialog window - this is fancier than JavaScript prompt
    var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;
    var dialog = new dialogClass('glide_confirm_standard');
    dialog.setTitle('Confirmation');
    dialog.setPreference('warning', true);
    var title = 'Are you sure you want to cancel the selected Worklfow Contexts?';
    dialog.setPreference('title', title);
    dialog.setPreference('onPromptComplete', callback.bind(this));
    dialog.render();
}

 

o. Click on the Submit button to save your work.

 

2. UI Action.png

 

We are now ready to set up our test, and test our our new UI Action!

 

 

Lab 1.3: Testing

 

Finally we will create bad workflow that will hang, and fire it off a few times.

 

1. Navigate to: Workflow -> Workflow Editor.  The Workflow Editor will be displayed in a new browser tab.

2. From the Workflows tab on the right, click on the "+" button to create a new workflow.  The New Workflow form will be displayed.

3. Fill in the form with the following:

a. Name: Broken Workflow

b. Table: Global

c. Click on the Submit button to create your new workflow

4. From the Workflow Editor navigate to Core -> Utilities and drag out a new Run Script Activity

a. Name: Initialize

b. Script: gs.log('---> Broken Workflow!', 'Broken Workflow.Initialize');

c. Click on the Submit button to save your activity.

5. From the Workflow Editor navigate to Core -> Approvals and drag out a new Approval - User Activity

a. Name: Do Nothing Approval

b. Users: Add Beth Anglin to the list

c. Click on the Submit button to save your activity

6. Wire up your workflow to look like this:

 

3. Broken Workflow.png

 

7. Run the workflow five times.  Since Beth is not around to approve things this effectively "hangs" the workflow.

8. From your ServiceNow instance navigate to: System Logs -> All.  The list view of log entries will appear.  Filter on Source Contains Broken.  You should see your workflow logging itself.

 

4. Broken Workflow Logs.png

 

8. From your ServiceNow instance navigate to: Workflow -> Active Contexts.  The list view of active contexts should appear.

9. Filter on Workflow Version = Broken Workflow.  Your five test workflows should be shown with State of Executing

 

5. Hung Workflow Active Contexts.png

10. Check a couple of the list view check boxes to select the workflows you want to cancel. 

11. Click on the Actions on Selected Rows dropdown at the bottom of the list view.  A menu will appear displaying list action choices.

 

6. Pick Two and Choose Cancel.png

 

12. Click on the Cancel Workflows link.  A dialog box will appear asking if your are sure you want to cancel the chosen workflows.

 

7. Are you sure.png

 

13. Click on the Ok button to confirm you want to cancel the workflows.

14. The list view will be refreshed, and you should be down a couple of executing workflows!

15. Navigate to: System Logs -> All and filter on Message Contains Attempting.  This will show you the logging from the Script Include.

 

8. Broken Workflow Logs.png

 

16. Go ahead and cancel the remaining workflows using Steps 10 through 12.

 

So there you have it!  A List Choice UI Action that allows you to cancel multiple executing workflow contexts from a List View.

 

I went ahead and placed this solution out on the ServiceNow Share: here

 

Steven Bell

 

 

If you find this article helps you, don't forget to log in and "like" it!

 

I would also like to encourage you to become a member of our blog!

Filter Blog

By date: By tag: