The Now Platform® Washington DC release is live. Watch now!
07-30-2018 03:13 PM - edited 12-17-2023 04:17 PM
NOTE: MY POSTINGS REFLECT MY OWN VIEWS AND DO NOT NECESSARILY REPRESENT THE VIEWS OF MY EMPLOYER, ACCENTURE.
DIFFICULTY LEVEL: INTERMEDIATE
Assumes good knowledge and/or familiarity of Orchestration, Workflows, and Scripting in ServiceNow. Assumes having taken the class SSNF and has good intermediate level of knowledge and/or familiarity with Scripting in ServiceNow.
One thing I have gravitated to, over the years as a Workflow/Runbook/Orchestration developer, is thinning out my scripted Workflow Activities. What this means is that to better maintain the code base in any given workflow it is a best practice to move the code into a Script Include library, if possible. I usually end up creating a Workflow Utils named library of functions. There are several reasons to do this:
So, the best practices?
The question that comes up is: So how do I implement Script Includes with workflows?
Easy. Just follow this simple lab to get started!
Work through the following Mini-Lab article: Mini-Lab: Printing off the Workflow Scratchpad object as we will be using the workflow developed there.
Name: ML_WorkflowUtils
Accessible from: All Application Scopes
Description:
Utilities to be used from Workflows/Orchestration
Usage example:
var utils = new global.ML_WorkflowUtils();
utils.loadScratchpadObject(current, workflow.scratchpad);
Script:
var ML_WorkflowUtils = Class.create();
ML_WorkflowUtils.prototype = {
initialize: function(location) {
this.location = location;
},
// convert the current.variables object into the
// workflow scratchpad object.
// btw, this is the first step to making a workflow Service Catalog variable agnostic.
loadScratchpadObject : function(current, scratchpad) {
var message = 'current.variables Object: \n';
var variables = current.variables;
for (var item in variables) {
message += item + ': ' + variables[item] + '\n';
var name = item.replace('v_','');
scratchpad[name] = variables[item];
}
gs.info('---> [{1}] {0}', [message, this.location]);
},
// now loop through the workflow scratchpad object and write all the values
// to the System Log.
writeScratchpadObjectToLog : function(scratchpad) {
var message = 'Scratchpad Object: \n';
for (var item in scratchpad) {
message += item + ': ' + scratchpad[item] + '\n';
}
gs.info('---> [{1}] {0}', [message, this.location]);
},
type: 'ML_WorkflowUtils'
};
4. Save your Script Include. We are done with the library.
1. Navigate to Workflow > Workflow Editor
2. Edit up the workflow: ML - Print Scratchpad Object (from the previous article)
3. Open up the Run Script Activity and change the code to the following:
var location = context.name + '.' + activity.name;
// The activity location is passed in at instantiation of the object
var utils = new ML_WorkflowUtils(location);
// load the scratchpad object
utils.loadScratchpadObject(current, workflow.scratchpad);
// print of the filled scratchpad object
utils.writeScratchpadObjectToLog(workflow.scratchpad);
Note: We have replaced the previous code with calls to the Workflow Utilities Script Include.
4. Click the Update button to save the activity.
NOTE: We have now pushed the code down to a Script Include library. "Thinned" out the code in the Activity so that it really does not require an maintenance in the workflow. In the process this has now achieved our goals of Maintainability, Reusability, and Searchability! Any future modifications to the code will be made to the Script Include and will not require editing the Workflow.
NOTE: Since workflow.scratchpad is a Javascript object it is passed by reference (pointer), and therefore any modifications made to the object in the Script Include are reflected immediately in the original object. Thus when we go to print out the workflow.scratchpad object we find that it is now populated.
1. Navigate to Service Catalog > Catalog Definitions > Maintain Items
2. Search for and open the item: Scratchpad Variable Sender
3. Click on the Try It button. This will bring up the new Service Catalog Request form.
4. Fill out the form with any random text and click the Order Now button.
5. After the Order info form appears navigate to System Logs > System Log > All.
6. Search for '--->' in the messages column.
7. You should see something like this in the logs:
Look familiar? It should be exactly the same as in the previous article only you are executing code in the Script Include instead of the Workflow Activity.
Congratulations you now have created your very first Workflow Utility Script Include library! Keep adding functions to it and I am sure you will continue to find ways of improving your own code to make it more useful!
Enjoy!
Steven Bell.
If you find this article helps you, don't forget to log in and mark it as "Helpful"!
Originally published on: 07-30-2018 05:13 PM
I updated the code, fixed broken links, and brought the article into alignment with my new formatting standard.
Hi Sabell2002,
I've recently become active in the boards and have found your posts to be some of the more fun to read.
Another approach to working with Workflows and Script includes can be:
Instead of creating a prototype object then instantiating it, as long as this is in the global scope, create a script include:
var ShowMeGroupsWF = {
printScratchPad: function() {
gs.log( activity.name + " " + context.name + " " + workflow.scratchpad + " " + current.getDisplayValue());
}
};
and call it like so from the activity:
ShowMeGroupsWF.printScratchPad();
The result will be:
Activity Name: Call SI, Context Name: ShowMeGroups, Scratchpad: {"whoAmI":{"props":["one","two","three"]}}, Current: RITM0010020
Having the execution context readily available allows to bypass some of unnecessary JavaScript calls, making the code cleaner, etc.
Thanks! The purpose of this article was to show the Script Include best practices, and the object pass by reference. I like your idea. It might get messy when you have really large scratchpad objects (which I often do).
Steven.
Certainly, large objects in text format are nearly impossible to discern. They might get cut off in the logs as well. Which then leaves only the option of printing properties.
Because I mainly develop SNow code outside the plaform, my preference is for viewing entire objects rather than the parts. This is purely for inspecting relationships to visualize strategy. This, of course, is easier in forms than in logs. The larger the object the more I want to see the overall structure.
In all, I prefer to:
1. Print whole JSON string to the logs when possible.
2. Grab and paste it in a JSON formatter to inspect.
3. Side by side comparison of code and JSON string.
4. Only if I'm "pseudo coding" are properties printed or/ print critical properties when debugging.
Printing properties then highlights "log management"? How can logs become an useful tool tool, rather than a hindrance. I worked for a company that rendered logs in all of their instances useless by printing unnecessarily. Perhaps that can be another subject, if you already haven't, that you can write about?
Yeah, log management is one of my hot buttons. Check out these articles, but yeah, I probably need to re-iterate the need to log only mostly for debugging purposes (it has been awhile since I have written on the topic).
Community Code Snippets - Logging: Some Notes on Business Rules
Community Code Snippets: Using a System Property to Control Logging
Community Code Snippets - Logging: Some Notes on Workflows (handling mystery messages)
Community Code Snippets - System Log Ordering Problem Workaround (cleaning up the logs)
Thanks!
Steven.
Just to add on the topic of object literal vs prototype.
If you working with a single object that's fine but as soon as you start dealing with a hierarchy of objects you have to use the prototype - unless you want to stick with deprecated Object.extend which is not recommended or writing your own handlers for delegation.
Regards
Greg
Correct. You can follow the pattern or try to reinvent the wheel.
Regards
Greg
I might not get the comment fully.
PrototypeJS Monkey Patched JavaScript's Object
SNow Monkey Patched PrototypeJS
Yet bypassing Monkey Patches in favor of using pure JavaScript methodology to solve undesirable re-inventions of the wheel, leaving code ready for future Rhino engine upgrades, is itself re-inventing the wheel? I tend to consider those a return to basics.
Both, Composition and a shim of Object.create are industry standard methods that prevent implementations that quickly become obsolete, such as Object.extends.
Perhaps I am indeed wrong by looking at it that way
When in Rome ...
Regards
Greg
haha. love the comment!
SPARTA 🙂