The Now Platform® Washington DC release is live. Watch now!

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
sabell2012
Mega Sage
Mega Sage

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:

 

  1. Reusable Code
  2. Ease of Maintenance
  3. Easily searchable

 

So, the best practices?

 

  • By definition a library of functions is for future reusability. So that Best Practice is obvious. 
  • Ease-of-Maintenance flows from that by making the code centralized, and generic (a reduced and refactored code-base with comments!).
  • Maintenance is also improved by allowing the developer to make modifications to the code without having to check out the workflow. 
  • Easily Searchable is one that most developers don't realize is missing from Workflow activities. There is no easy-to-find searchable code base. It is hidden and hard to get at! This last one is a biggy, and a Script Include Library will instantly solve it. BTW, it is findable in sys_variable_value.

 

The question that comes up is: So how do I implement Script Includes with workflows?

 

Easy.  Just follow this simple lab to get started!

 

Lab 1.1: Workflow Script Include Example

Pre-requisite

Work through the following Mini-Lab article: Mini-Lab: Printing off the Workflow Scratchpad object as we will be using the workflow developed there.

 

Creating The Workflow Script Include

  1. Navigate to System Definition > Script Includes.
  2. Click the New button.
  3. Fill in the form with the following:

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.

 

sabell2012_0-1702855242690.png

 

Calling The Workflow Script Include

1. Navigate to Workflow > Workflow Editor

2. Edit up the workflow: ML - Print Scratchpad Object (from the previous article)

 

sabell2012_0-1702858089808.png

 

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.

 

sabell2012_1-1702858160606.png

 

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.

 

Testing The Workflow Script Include

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.

 

sabell2012_2-1702858268231.png

 

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:

 

sabell2012_3-1702858531926.png

 

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"!

 

sabell2012_0-1702753602928.png


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.

Comments
Javier Arroyo
Kilo Guru

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.


Kevin Eldridge
Kilo Expert
I also enjoy reading Steven Bell's posts. Always very informative. This topic is something I have been working on in my spare time. Putting code into reusable script includes. I like the idea of using this for your workflow. Much easier to maintain. Thank you Steven for sharing!
sabell2012
Mega Sage
Mega Sage

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.

Javier Arroyo
Kilo Guru

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? 

sabell2012
Mega Sage
Mega Sage

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.

Greg42
Kilo Guru

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

Javier Arroyo
Kilo Guru
The Object literal may become the prototype for other objects, bypassing the need for PotorypeJS style functionality, or delegation. I suppose it depends on use case. ...thinking libs as object liberals... then have a constructor object use them through composition, or implement the sort of idea executed by JS5 Object.create where objects are linked to other objects.
Greg42
Kilo Guru

Correct. You can follow the pattern or try to reinvent the wheel.


Regards

Greg

Javier Arroyo
Kilo Guru

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

Greg42
Kilo Guru

When in Rome ...

Regards

Greg

Javier Arroyo
Kilo Guru

haha. love the comment!

SPARTA 🙂

Version history
Last update:
‎12-17-2023 04:17 PM
Updated by:
Contributors