- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 10-31-2018 09:17 AM
How much do we understand of every line of code that we write when extending ServiceNow funcitonality? Each script we create is something that will later be maintained by someone else, and they will have to work out what the original intention was and see how to modify it to fit a new requirement or to work in a platform that is continually evolving. There is lots of good best practice already writen that describes when we shouldn't script or ways to use particular Glide libraries. Hopefully I can add a little here about some JavaScript patterns I've found useful and some practices I employ while coding...
The title or this article is from "Thing Explainer: Complicated Stuff in Simple Words" by Randall Munroe - see his comics at xkcd.com.
Occasionaly I need to find out something about an object that is available to me at a specific point in the platform (maybe it's a lesser documented area or I've got to implement something out of the ordinary). What I'll do here is drop in a function that describes the properties and methods of the object - and spit out a gs.info of the information I've compiled.
This is the Thing Explainer, and it's got a pile of useful concepts in it. In the end we will have a bit of a Swiss Army Knife, but some places we might temporarily use it to give a quick help are:
- In a Business Rule we could log out all the properties of current and previous
- When running an Import Set Script there are different variables available to us in each type of script and it's useful to find out what the contents of each one are
- In a workflow 'Run script' Activity the 'workflow' variable has lots of useful information and it's useful to go beyond the documentation to find out what this variable makes available
- I was recently working on SLA calculation to override default behaviour, so in the depths of the Script Includes of this area it's useful to find out what an object actually is
Starting in a Background Script we try things out by grabbing an incident and describing the properties of the GlieRecord:
/*
* A 'Thing Explainer' function, which is given an object,
* it compiles a list of all the properties of the object with their values
* Returns the properties as a string
*/
function thingExplainer(obj) {
var log = '';
// Use the JavaScript built-in function to get an Array of the property names on our object
var props = Object.getOwnPropertyNames(obj);
for ( var i=0; i<props.length; i++ ) {
var prop = props[i];
// Add each property name and it's value to the output string
log += prop + ' - ' + obj[prop] + '\n';
}
return log;
}
// Get an incident record - you may want to substitute a different sys_id
var grInc = new GlideRecord('incident');
// Use the 'Thing Explainer' to log the content of the incident
if ( grInc.get('85071a1347c12200e0ef563dbb9a71c1') ) {
gs.info(thingExplainer(grInc));
}
Now to take the above apart and explain (some of) the 'Thing Explainer'...
Getting a Record to work with
Starting at the end, the last 6 lines should be fairly familiar, but some details are:
- Use GlideRecord's 'get' function whenever retrieving a single record
- The '.get' function can take one argument (the sys_id of the record to look for) or two arguments (the field name and a value to match) to do a search that returns the first record found
- I've seen places where the .get function is used and it is not wrapped in an 'if' statement, but as 'get' returns 'true' when it finds a record, then the 'if' statement makes sure that the 'grInc' is the record we were looking for
- Variable names are important as they help the code to be more readable - we've tended to put 'gr' at the front of the name for all GlideRecords, but to also make sure there is some of the table name in there too, hence 'grInc', though you could use 'incident'
- Always use a block (enclosed in '{' and '}') with an 'if' statement - for consistency and for when someone edits things later
- A background script is one of the few places where it is acceptable to use a hard-coded sys_id
Next some details on our 'thingExplainer' function:
Comments
It's useful to have a comment that describes a function, making sure to state what the expected parameters are, what the function does and what it returns
/*
* A 'Thing Explainer' function, which is given an object
* Compiles a list of all the properties of the object with their values
* Returns the properties as a string
*/
The Function
Before we move on to the body of the function, let's just check we understand what an 'object' is. An object is a thing we want to refer to in our virtual world, we want to do stuff with the object. In JavaScript objects have a collection of properties, where each property has a name and a value. In ServiceNow we frequently work with GlideRecord objects that represent records used in processes (e.g. incidents) and the properties of these objects are the same as the fields in the records (e.g. priority or caller_id).
There is a lot more complexity available in JavaScript objects, but for now we'll simply use them as a collection of properties that each have a name and value. We can leave prototype-based inheritance for another article...!
var props = Object.getOwnPropertyNames(obj);
for ( var i=0; i<props.length; i++ ) {
var prop = props[i];
// Add each property name and it's value to the output string
log += prop + ' - ' + obj[prop] + '\n';
}
- 'Object' is built into JavaScript and has a function called 'getOwnPropertyNames', which returns an Array (a list) of the property names of the given object
- We'll use a 'for' loop to work through (iterate over) each of the properties, starting with i=0
- The variable 'prop' gets the name of the property that we are currently working with, e.g. 'assignment_group' or 'urgency'
- For each property name we add a line to the log of the format '<property name> - <property value>\n' - the '\n' is a new line
- To get the value of the property we use array notation 'obj[prop]', so if 'prop' is 'assigned_to', then this is the same as doing 'obj.assigned_to', and then next 'prop' might be 'assignment_group' so we get the value of 'obj.assignment_group'
I'd like to take this and put it into a Script Include so that we can re-use it when needed. Then we can add some additional parameters to control where the output goes. Finally we'll use the 'Thing Explainer' to drill down into the background of the object or even traverse through to other objects that are hanging off this one (like the 'dot walking' we are used to in ServiceNow). So, there's more to come...
- 1,152 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This is theoretically very cool, but it doesn't work in the one place I need something like this: workflows.
Every activity in a workflow has access to the workflow object named "workflow". I would like to be able to retrieve the context ID of the currently executing workflow context from within a Run Script activity. This would allow me to dynamically generate output for errors using a single activity (or two) versus needing to duplicate the error/output handling for each possible place where an error can occur. But I receive no output whatsoever if I try and run the code you've provided within the run script activity using the workflow object.
I had actually tried running some of my own code with the "getOwnPropertyNames()" function before finding this, but got the same result with both.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
If the object is large you can get "org.mozilla.javascript.EcmaError: Cannot find default value for object."
Solution: obj[prop].substring(0,80) + ...
For example the email object in an Inbound Action script.