Skip navigation

One of the biggest issues I see when supporting code is the lack of code formatting in custom scripts.  This can be potentially a serious issue, and I have seen it lead to some bad mistakes!  Here I will cover some of what I consider best practices in coding format.  I will present the good, the bad, and the ugly! :-)

 

Indentation

 

Indentation allows a coder to quickly see variable relationships, variable scoping, function placement, open and closed brackets placement, and … well you get the idea.  It vastly improves readability.  Again don’t be of the OLD school:  If it hard to write it ought to be hard to read!  This simply does NOT work and is not a good software engineering practice!

 

I like code to be indented in where appropriate by 3 to 4 spaces (or a tab covering that amount).  The editor format button is your friend!

 

 

Example 1:  Badly indented code.

 

And before you ask I have seen this type of stuff in real life.

 

var results = getResults();

function getResults() {

var incidents = new GlideRecord('incidents');

incidents.query();

while(incidents.next()){

gs.print(incident.number+'');

if (incident.number=='INC0000012')

break;

if(incident.number=='INC0000012')

gs.print('hello world');

}

}

 

Example 2: Ugly

 

And see things like this as well!

 

var results = getResults();

function getResults() {

var incidents = new GlideRecord('incidents');

incidents.query();

while(incidents.next()){

gs.print(incident.number+'');

if (incident.number=='INC0000012')

break;

if(incident.number=='INC0000012')

gs.print('hello world');

}}}

 

 

 

Example 3: Properly indented code

 

Much better!  White space is cheap.  Use it a lot, use it wisely!

 

var results = getResults();

 

function getResults() {

    var incidents = new GlideRecord('incidents');

    incidents.query();

 

    while (incidents.next()){

        gs.print(incident.number+'');

 

        // brackets even around one line improve readability

  // AND allow for easier insertion of debugging statements.

        if (incident.number=='INC0000012') {

            break;

        }

 

        if (incident.number=='INC0000012') {

            gs.print('hello world');

        }

    }

}

 

Formatted you can actually see the hierarchy of the code!  Readability for maintenance!

 

 

Formatting

 

Example 1:  Badly formatted code

 

Really the bad and the ugly are the same here.

 

var incidents=new GlideRecord('incidents'),mainCount=0;incidents.query();

if(incidents.next()){gs.log(incident.number+’’;}

 

Example 2:  Good

 

 

Really the bad and the ugly are the same here.

 

 

var mainCount = 0;

 

var incidents = new GlideRecord('incidents')

incidents.query();

 

if (incidents.next()) {

    gs.log(incident.number+’’;

}

 

 

White Space

 

 

Whitespace is good.  Don’t worry about adding an extra line here or there, or putting spaces where it makes sense for readability:

 

 

Bad:

 

 

if(myHat==’green’){}

for(var i=0;i<checkList.length;i++){}

gs.print(‘incident number:’+incident.number+’,’+incident.short_description+’.’);

while(incidentRecords.next()){}

 

 

Good:

 

 

if (myHat == ‘green’) {

}

 

for (var i=0; i < checkList.length; i++) {

}

 

gs.print(‘incident number:’ + incident.number + ’,’ + incident.short_description + ’.’);

 

while (incidentRecords.next()) {

}

 

 

Again: space is cheap.  Readability is all!

 

 

The Script Editor

 

Just a couple of side-notes on the out-of-the-box (OOtB) editor.  It isn’t too bad for formatting.  It has a couple of gotchas which I will explain, and throws formatter warnings on stuff that is not necessarily important.

 

NOTE: The formatter button is hard coded OOtB to indent exactly 4 characters (it inserts a 4-character tab).  There is no property to change this.

 

          Editor Quirk 1:  Comments Can Cause Bad Formatting

 

In some cases the format button gets confused when code is commented out.  Any following code is indented incorrectly.

 

Before:

After:

As you can see the comment seems to influence the indentation and appears to make the “assumption” that line 20 is part of the “if” in line 5.  Deletion of the comment on line 9 (and for that matter line 19) and clicking the format button again will straighten out the indentation.

 

 

Editor Quirk 2:  Switch statement causes bad formatting.

 

Before:

 

After:

It aligned all of internal code inside of the switch!  Fix:  Don’t click the format button if you have a “switch” statement in your code.  With this sort of thing I format it in my external editor (Notepad++, or Sublime work fine) and copy-and-paste my code into the script editor.

 

Spaces vs. Tabs

 

In some cases your editor will throw a warning about mixed spaces and tabs.  Ignore these.  They are not important (unless you are obsessive about such things).

 

 

So in summary:  Formatting is a best practice in code development.  Always keep in mind that you are probably not the only one that will be maintaining your code.  Don't leave a mess.  Good formatting habits are worth cultivating, and making a personal standard (as well as a corporate standard).  Use the editor format button as much as possible.  Keep in mind it has a couple of quirks!

 

Steven Bell.

Upgrading to Eureka or later versions, can give you some interesting messages and behavior thanks to the added ability to have more than one Service Catalog. In Eureka, Fuji and beyond, you may encounter a Page not found error message when you attempt to use the Continue Shopping and Back to Catalog buttons in your Service Catalog. You'll see a message that looks something like this:

 

Pagenotfound.JPG

 

This broken link error message is likely caused by the new functionality in Eureka, which introduced multiple service catalogs. The Catalog Site table associates the catalogs with the CMS websites. These catalog site records appear in the Sites related list on the Catalog form. Several service catalog system properties have actually been replaced with fields on the catalog site record. This allows you to specify values for different sites used by different catalogs. To correct this issue, you'll need to associate the CMS site with the Service Catalog to fix your UI buttons.

 

How to associate the CMS to the Service Catalog:

  1. Navigate to Service Catalog > Catalog Definitions > Maintain Catalogs.
  2. Click the Service Catalog record in use.
  3. Click the Sites related tab.


Edit.JPG

  1. Click Edit to open the slushbucket.
  2. Double-click your custom site on the left to add it to the right.
  3. Add this information in the Site record:
    • CMS homepage: the url suffix of the content page that is the top of your catalog
    • CMS search page: catalog_find_cms
    • CMS 'Continue Shopping' page: the url suffix of the content page that you want users redirected to. If nothing is specified, the default behavior is to redirect the user to the previous CMS page
      SiteRecord.JPG

You should now be redirected to the correct CMS content page without receiving an error message.

 

Having other Service Catalog UI button issues? See:

Redirecting the Back to Catalog button

Get the Continue Shopping button working in your Service Catalog

The Continue Shopping button in the shopping cart redirects to incorrect an URL (KB0535421)

"Back to catalog" button redirects to the incorrect catalog (KB0547169)

 

Need more Service Catalog tips? See:

Creating Catalog Client Scripts

Defining Catalog Items

Adding Content Items to Service Catalog

In my past blog posts I have discussed Two watermarks in incoming Email messages,  and Email Notification dependencies to their ‘Sys version’ and ‘Content type’ fields, now I will cover what happens when "null" appears on your incoming messages marked as Reply. This is quite common on custom tables where the 'number' field does not exist.

 

null_1.png

null.png

 

Incoming emails classified as reply

Incoming emails can be classified as new, reply or forward based on a set algorithm.

 

Incoming email can be classified as a Reply because there is a matching watermark (e.g Ref:MSG000001) in the subject or the body message. Be aware that other options exist (e.g. the In-Reply-to header, the reply prefix, etc).

 

When our mail process matches a reply email with a watermark, the email logs will show a message for the matching record. Here is an example:

 

For a reply email that matches an record the incident table, the reply looks like:

> Received id=<D24D4975.3646> Classified as reply to 'INC0000001' via watermark 'Ref:MSG0000568' in message

 

As you can see, it will correctly display a value.

By default, the Number field is the display value for all task tables.

 

However, on my u_test_01 table, the replies looks like:

> Received id=<D24D4974.3646> Classified as reply to 'null' via watermark 'Ref:MSG0000567' in message

 

After some review, the mail process classifying as reply is searching for the column name ‘number’ (<target>.number). If it is not defined, it is shown as “null.”

 

Incoming email logs are the important to understand the behavior of inbound email actions. They are visible on the 'Inbox' view on the email table. If you see "null" on your incoming messages classified as reply, just ignore the null message. The mail process is working just fine.

 

The message reads Received id=<ID> Classified as reply to '<NUMBER>' via watermark 'Ref:<WATERMARK>' in message

 

Example to demonstrate the dependency with the 'number' field

 

I have created an example with two cases to validate why some incoming email logs are showing "Classified as reply to 'null' via watermark 'Ref:MSGxx' in message” (or subject). I created a new table, a new notification and a new inbound action.

 

I've created a simple u_test_01 table with the following fields:

 

Field #1

 

 

 

Field #2

 

 

Type

 

String

 

Type

 

String

Column name

 

u_string01

 

Column name

 

u_string02

Max length

 

40

 

Max length

 

300

Display

 

CHECKED

 

 

 

 

 

On any table, if the field with the attribute 'Display' set to true (checked), the field "value" is the the information that other tables will display when referenced to this record.

Note the behavior with display fields:

 

Display field value

Reference field shows as

Area of confusion

This is an example

This is an example

It looks like a String. Only the underscore  distinguish them as reference fields

(empty)

(just blank)

It looks like the field is NULL. Some applications will return “null” as the display value

NULL

(just blank)

It could look like not defined

  (just blank characters)

   (it looks just blank)

It looks like the field is NULL

Sys_id of the reference does not exist

(just blank)

It looks like the field is NULL

 

Here is how it looks on the 'Test 01' table created for this example:

2015-10-22_1008-null-001.png

 

Then, I have created a email notification ‘u_string01 notifications’, that fires when u_string01 changes:

 

Name

=

u_string01 notifications

Table

=

u_test_01

Inserted

=

checked

Updated

=

checked

Conditions

=

u_string01 Changes

Users

=

Jxxx@xxx.com

Subject

=

Test u_string01

Body

=

Test u_string01

 

Finally I have created a simple inbound action as follow:

 

Name

=

u_test_01_update

Table

=

u_test_01

Type

=

Reply

Condition

=

current.getTableName() ==  'u_test_01'

 

Script =

// This entry is to force a change on the record. 
// If you set a field to the same value or make no changes, 
// it will not execute update() 
current.u_string_02="this have been updated from: " + sys_email.sys_id;

// This update is required to get target set on the incoming email. 
// If no update happens, target is (empty)
current.update();  

//Logging the execution while developing. Remove on production
gs.log("u_test_01_update action executed", "EMAIL." + sys_email.sys_id);

 

Running the first CASE:

Tables without the 'number' field will display 'null' on the email logs

Note we have not added the 'number' field in the table yet.

 

When we received a reply email with Ref:MSG0000702 watermark in the subject, logs will show:

> Received id=<D24D4974.3646> Classified as reply to 'null' via watermark 'Ref:MSG0000702' in subject

> Processed 'u_test_01_update', updated u_test_01 :TEST01

2015-10-22_1008-null-004.png

The message "updated u_test_01 : TEST01" means the target is set to the matching record for the incoming reply message (otherwise it is empty). Again, no updates mean a target will be empty (see above for the "behavior with display fields")

2015-10-22_213-target.png

 

Running the second CASE:

Tables with the 'number' field will display the 'number' value on the email logs instead of the display value.

To execute the second test, I created a new field on the form with column name 'number':

 

Field #3

 

 

Type

=

String

Column name

=

number

Max length

=

40

 

Then updated the same data with 'number' = "NUMBER01."

2015-10-22_1008-null-005-with-number.png

 

After I sent the reply email to the instance with Ref:MSG0000702 in the email body, the logs showed:

> Received id=<D24D4975.3646> Classified as reply to 'NUMBER01' via watermark 'Ref:MSG0000702' in message

> Processed 'u_test_01_update', updated u_test_01 :TEST01

 

2015-10-21_1218-Watermark-null-06-correct-message.png

 

As you can see, it will correctly display a  value and it is the 'number' column. By default, the Number field is the display value for all task tables.

  • The column named "number" can only be created by ServiceNow developers.
  • Regular developers creating "number" will see the column renamed as u_number.
  • If you extend from task, you will inherit the number field and it will show correctly on the incoming message.
  • If you see "null"on your incoming messages, just ignore the message because the mail process is working just fine.

 

Our mail process searches for the number field on that target table to display classified as reply to '<target.number>' via watermark. I tested this example in Fuji using Chrome as my internet browser. I was able to successfully set a Display value on a table, determine why a target shows up as empty, and how "null" appears on an incoming email.

 

More information can be found here:

 

Thanks joaorodrigues

While pondering all the modifications I had made to date I realized that I still had not arrived at all of my targeted functionality.  I still wanted the same ability as I would get in a true SQL SELECT statement.  Namely to specify which fields were returned.  In other words proper SQL SELECT capability.  While SELECT * was fine I wanted SELECT field1, field2, ..., fieldn!

 

I played around trying to get a .addSelect(field1, etc.);, but was unable to come up with something I liked; let alone worked right!

 

I settled for something not as nice, but that still worked.  I ended up adding a new prototype method to my GlideRecordExtensions Script Include to do the deed.

 

This is the format:

 

var <<object array>> = <<glideRecord>>.select('field1, field2, ..., fieldn');

 

The select method will return an object array containing each record, but with only the fields specified.

 

What follows is how to add this functionality.

 

 

Prerequisites:

 

1. Do the JavaScriptExtended lab if you have not already done so.  We will be adding new functionality to the ServiceNow JavaScript code base.

 

2. Do the GlideScriptExtended lab.  This will be the Script Include we will be extending to give us the new functionality for GlideRecord.

 

3. Familiarity with Scripts - Background to run a simple test.  Article.

 

4. Familiarity with Fix Scripts to run our final test.  Article.

 

 

Lab 1.1 - Add Array.indexOf to the JavaScriptExtended class.

 

We need a way to search a one-dimensional array for a specific value.  Array.indexOf added in ECMAScript 5 does what we want.  Since indexOf for arrays is missing from the ServiceNow code base (remember it is ECMAScript 3) we will need to add it to our JavaScriptExtended Script Include.

 

1. In your browser go to the MDN definition page for Array.indexOf:

 

Array.indexOf

 

2. Copy out the Polyfill definition code (scroll down a bit to see it on the page, and copy only the code).

 

2.polyfill.JPG

 

3. In ServiceNow navigate to Script Includes and edit the JavaScriptExtended Script Include.

 

4. Paste the Polyfill into the end of the script and save your work.

 

3.javascriptextensions array.indexof.JPG

 

5. Raise your security level to Security Admin, navigate to Scripts - Background and run the following test to verify the functionality has been added to the language base.

 

  gs.include('JavaScriptExtensions');

  var fieldList = ['cmdb_ci_server', 'cmdb_ci_moose', 'cmdb_ci'];

  var findField = fieldList.indexOf('cmdb_ci');

  gs.print(findField);  // result should be 2

 

 

1.Scripts-Background.JPG

 

Now we are ready to extend GlideRecord!

 

 

Lab 1.2 - Add Select to GlideRecordExtension

 

With this lab we will be adding our new select method to the existing GlideRecordExtension Script Include.

 

1. Navigate to Script Includes and edit the GlideRecordExtension Script Include.

 

2. Add the following new function to the end of the Script Include.

 

NOTE: I have introduced a new wrinkle in functionality.  If you add a variable to the "this" object in a prototype method it becomes a global variable to the GlideRecord object (or any object where this is done) when the new function (.select) is called the first time.  When we assign a value to such a variable; that value is now available to all following function calls.  So in this case isSelect and selectFields will be available later to other functions called inside of GlideRecordExtended.

 

 

  GlideRecord.prototype.select = function(selectFields) {
      this.selectFields = [];  // initialize our global array
      this.isSelect = true;  // turn on the filter
      this.selectFields = selectFields;
      var selectList = this.toObjectList();
      this.isSelect = false;  // turn off the filter
      return selectList;
  };

 

NOTE:  It is considered a best practice to initialize your this variables (or any variables for that matter).

 

3. Modify the GlideRecord.prototype.toObject function to the following in the Script Include.

 

GlideRecord.prototype.toObject = function(recordToPackage) {
    var packageToSend = {};
    for (var property in recordToPackage) {
        try {
            if ((this.isSelect && this.selectFields.indexOf(property) > -1) || !this.isSelect) {
                packageToSend[property] = recordToPackage[property].getDisplayValue();
            }
        }
        catch(err){}
    }

    return packageToSend;
};

 

This new "if" check looks to see if we are doing a "select" AND if the specific field is one we are wanting, otherwise retain our old functionality.

 

4.gliderecordextensions array.indexof.JPG

 

 

Now we are ready to test our new select function!

 

 

Lab 1.3 - Test the Gliderecord.select Function

 

1. Navigate to Fix Scripts and create a new Fix Script

a. Name: GlideRecord Select Test

b. Active: Checked

c. Run Once: Checked

d. Description: This Fix Script is for testing the GlideRecord.select function

e. Script:

 

gs.include('GlideRecordExtensions');  // include our library of new functions

// this is our test recordset of 10 records
var incidents = new GlideRecord('incident');
incidents.addActiveQuery();
incidents.setLimit(10);
incidents.query();

// Execute our new select function. Ask for just the sys_id and number fields.
var incidentList = incidents.select('sys_id, number');

// Loop through and print out our resultant object array
for (var i=0; i < incidentList.length; i++) {
    gs.info('{0} - {1}, {2}', 
        incidentList[i].number,
        incidentList[i].sys_id, 
        incidentList[i].assigned_to /* not in select list */);
}

gs.print(' ');

// Execute the toObjectList function to retrieve ALL fields
var incidentFullList = incidents.toObjectList();

// Loop through and print out our resultant object array
for (var i=0; i < incidentFullList.length; i++) {
    gs.info('{0} - {1}, {2}', 
        incidentFullList[i].sys_id, 
        incidentFullList[i].number,
        incidentFullList[i].assigned_to);
}

incidents = null;  // Drop our heavy object from memory

 

Your script should look something like this:

 

5.test array.indexof.JPG

NOTE: You will notice I added a line to the end of the test.  This shows how to get rid of the GlideRecord object from memory as it is no longer needed.  See the following article for more on memory management.

 

 

2. Run the Fix Script.

 

Results:

 

6.indexof test results.JPG

The first part of the test verifies that our new function is working correctly.  The select method goes out and only retrieves the specified fields and nothing else.  Since assigned_to was not in our select fields list then it was not retrieved and returns "org.mozilla.javascript.Undefined".

 

The second part of the test verifies we have not broken our old functionality and that everything is returned.  The assigned_to field will be properly filled in.

 

End of testing!

 

There you have it!  Not a perfect solution, but the best we can do for the moment.  If you DO figure out a way to implement a GlideRecord.addSelect as part of the actual GlideRecord query let me know!  I promise to do the same if I figure out a better way.  :-)

 

BTW, I have updated the Share with these latest modifications.

 

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!

CS_logo.jpgMVP-logo.jpeg

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

 


for Click for More Expert Blogs and also Find Expert Events!


The software that runs this community was upgraded yesterday. This gives a few new opportunities to get the most out of the community experience here.

 

The “My View” page (next to the home icon in the nav bar) is a great resource to use as your primary view on the community. It is customizable with tiles that give you shortcut views to different kinds of information.

 

To edit your view, click the “Edit page” link at the upper right. This allows you to edit the page which has a lot of “tile” areas. It’s similar to editing home pages in ServiceNow. Click on a “Add a tile” link and you can choose which type of content to populate that area. You can also reorder them by dragging, or move up and down one with the icons in the upper right of each tile. If that tile has some specific configuration (like choosing categories or tags), you can access that with the gear icon in the upper right.

 

Central section tiles that you can use to optimize your experience are the “Frequently viewed” and “Recently viewed” tiles. You can configure them to show content, people and/or places in each of them. On creation of the tile or by clicking the gear icon, you will presented with a set of check boxes to choose which of the three the tile will display (and you need at least one checked.)

Screen Shot 2015-10-23 at 10.25.04.png

 

The sidebar tiles have some more choices available than the central tiles. You can set up tiles for “Tagged Content”. This allows you to specify a comma separated set of tags that you are interested in, and see the most recent posts with that tag. For example, “geneva” would be a great tag to follow now, along with others of relevance to your current work. The most popular tags right now are “catalog”, “notifications”, “script”, “cms” and “workflow”. This also implies that when you are posting discussions and questions, tagging them well is a good idea as it might help surface your question to people interested in following the topic. It is possible to see the tag cloud of the top 200 most commonly used tags here.

 

Screen Shot 2015-10-23 at 10.26.38.png

 

Some of these tiles are automatically populate, like the “Frequently viewed”, “Trending” and “Tagged content.” Some of these tiles consist of static content you manage yourself. You can do this with the “Key Content and Places” tile. It allows you keep a scratchpad like area of topics you are following currently, as well as places (categories) you frequently read. Similar to this is the “Helpful Links” tile, which does much the same thing but with arbitrary links around the web. You can have a shortcut to HI, the Wiki or the API explorer right there in your sidebar on the community. By configuring it with your common destinations, you can make the “My View” of the community act as your central launching pad for your ServiceNow experience.

Screen Shot 2015-10-23 at 10.27.03.png

 

There is a lot of great functionality and a lot to learn in this new release of the community software. Here’s hoping it enhances your experience in the developer community. If you have any tricks or configurations that are making your life better, please leave them in the comments.

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

NOTE:  Recently, 10/31, I discovered that a number (85) of the following links were broken.  Lisa Latour lnz001 cleaned them all up for me!  Thank you Lisa!  All the links should be functioning again.  Thanks!

 

MOST RECENTLY PUBLISHED:

 

ServiceNow 150 Article & Video Collection - Two Year Anniversary - 7/27/2017

K17 - MVP Table Talk - Discovery vs. Service Mapping, Best Practices When Using Studio - 5/5/2017

K17 - CreatorCon: Advanced Orchestration: Create Run Powershell, and Run SSH Custom Activities - 5/5/2017

K17 - CreatorCon: Advanced Service Mapping - 5/5/2017

 

UPCOMING:

 

Ask the Expert - Service Mapping!

____________________________________________________________

 

 

Book Reviews

Book Report - JavaScript: The Good Parts - 9/29/2016

Book Report - Principles of Object Oriented JavaScript - 10/4/2015

Book Report - You Don't Know JS: Up & Going - 10/6/2016

 

Business Rules

Community Code Snippets - Logging: Some Notes on Business Rules - 7/8/2016

Mini-Lab: A Query Business Rule Demonstration - 10/20/2015

Mini-Lab - Limiting Field Display With a System Property - 9/14/2015

 

Configuration Management

Mini-Lab: Displaying all Affected CIs on a Business Service Form  - 10/2/2016

Mini-Lab: Displaying all Affected CIs on a Problem Form - 10/4/2016

Mini-Lab - Implementing a Relationship Between the Software Package and SAM Software Installs - 9/25/2016

Pragmatic Patterns – Basic Business Service Management Patterns  - 9/20/2016

Pragmatic Patterns – Hybrid Business Service Management Patterns - 9/22/2016

 

Database Views

Ask the Expert: Creating a Database View with Steve Bell - 8/4/2016 (video)

ServiceNow Admin 101: Creating a Service Catalog Variable Report - Originally 9/23/2014, re-publication: 5/13/2016

ServiceNow Admin 101: Observations on Database Views, Part I - Originally 7/14/2014, re-publication: 5/1/2016

ServiceNow Admin 101: Observations on Database Views, Part II - Originally 7/17/2014, re-publication: 5/3/2016

 

Discovery

Community Code Snippets: Removing a MID Server - 1/21/2016

ServiceNow Discovery 101: Setting Up a Local MID Server - Originally 8/21/2014, re-publication: 4/18/2016

ServiceNow Discovery 101: Virus Scanners And MID Server Performance  – Originally 1/20/2015, re-publication: 5/19/2016

 

Development

Mini-Lab - Analysis Example: Chasing Down Hidden Data - Part 1 - 4/9/2017

Mini-Lab - Analysis Example: Chasing Down Hidden Data - Part 2 - 4/13/2017

 

Email

Mini-Lab: Email Notification Scripts  - 11/8/2015

Mini-Lab: Adding CSS To an Email Notification - Part 1 - 11/10/2015

Mini-Lab: Adding CSS To an Email Notification - Part 2  - 11/12/2015

 

Field Normalization

Community Code Snippets - Simplifying Code With Field Normalization  - 8/29/2016

 

General

ServiceNow 100 Article Collection - One Year Anniversary - 7/22/2016

Community Code Snippets:  Creating Your Own AD Server  – 10/27//2015

Community Code Snippets - Requesting a Plugin for Your Personal Instance  – 9/1/2015

Community Code Snippets - Requesting Your Personal Instance – 9/1/2015

ServiceNow 150 Article & Video Collection - Two Year Anniversary - 7/27/2017

 

GlideRecords

Community Code Snippets - A Method of Reworking a GlideRecord Inside a Loop – 8/11/2015

Community Code Snippets - Current Factory - 8/27/2015

Community Code Snippets - Doing a SELECT DISTINCT(*) on a GlideRecord – 8/23/2015

Community Code Snippets - A Method of Reworking a GlideRecord Inside a Loop – 8/16/2015

Community Code Snippets - Four Ways To Do An OR Condition – 8/13/2015

Community Code Snippets - GlideRecord - Bracket Notation Reference vs. eval – 8/25/2015

Community Code Snippets - GlideRecord to Object Array Conversion – 9/24/2015

Community Code Snippets - Resetting a GlideRecord Back to the Beginning  – 8/9/2015

GlideRecords with Steven Bell : Ask the Expert - 7/29/2016 (video)

Mini-Lab: Adding a Select <<field>> to the Extended GlideRecord  - 10/26/2015

Mini-Lab: Extending the GlideRecord Object – 10/6/2015

Mini-Lab: GlideRecord Between Query Extension  – 10/11/2015

ServiceNow Scripting 101: Encoded Query - A Breadcrumbs Issue - Originally published 7/14/2015, re-publication: 5/21/2016

ServiceNow Admin 101: How to Do a UNION Query Using GlideRecord - Originally published 12/11/2014, re-publication: 5/23/2016

ServiceNow Admin 101: You Too Can Do DISTINCT Queries Using GlideRecord  - Originally published 11/11/2014, re-publication: 5/17/2016

 

Knowledge

K16 CreatorCon - Advanced GlideRecord Scripting - 5/4/2016

K16 Table Topics - Debugging Server-Side Scripts, and Development Process - 5/6/2016

K17 - CreatorCon: Advanced Orchestration: Create Run Powershell, and Run SSH Custom Activities - 5/5/2017

K17 - CreatorCon: Advanced Service Mapping - 5/5/2017

K17 - MVP Table Talk - Discovery vs. Service Mapping, Best Practices When Using Studio - 5/5/2017


Orchestration

Mini-Lab: Adding an LDAP Listener to Monitor AD in VirtualBox - 6/28/2016

Mini-Lab: Orchestration - Bringing Back the Run PowerShell Activity - 9/11/2016

Mini-Lab: Orchestration - Bringing Back the SSH Run Command Activity - 9/18/2016

Mini-Lab: Orchestration - Calling a Script With a Custom Run PowerShell Activity - Part 1 - 9/13/2016

Mini-Lab: Orchestration - Calling a Script With a Custom Run PowerShell Activity - Part 2 - 9/15/2016

Mini-Lab: Creating your own AD Server with VirtualBox - 6/13/2016

Mini-Lab: Orchestration - Creating your own Windows 2008 Server with VirtualBox - 6/5/2016

Mini-Lab: Orchestration - Setting Up an Ubuntu Desktop on VirtualBox - 8/15/2016

ServiceNow Scripting 101: Calling a Sub-Workflow, Part I – Originally 9/10/2015, re-publication: 5/9/2016

ServiceNow Scripting 101: Calling a Sub-Workflow, Part II – Originally 9/22/2015, re-publication: 5/11/2016

ServiceNow Scripting 101: Orchestration Best Practices - Originally 6/2/2015, re-publication: 5/5/2016 (with mamann)


Process

Community Code Snippets:  Analysis and Prototype Best Practices for Developers - 12/12/2015

Community Code Snippets:  Design Best Practices for Developers - 12/18/2015

Community Code Snippets:  Development Standards and Best Practices for Developers - 12/24/2015

Community Code Snippets: Quality and UAT Testing for Developers - 12/26/2015

Community Code Snippets:  Requirements Best Practices for Developers - 12/10/2015

Software Engineering - Remember Risk! - 2/29/2016

 

Scripting

Ask the Expert: Posting Log Information from Client to the System Log via Ajax with Steve Bell - 9/27/2016 (video)

Community Code Snippets - Advanced Object Filter Example – 10/8/2015

Community Code Snippets - Converting to Local DateTime  – 7/22/2015

Community Code Snippets - Comparing Two Times Using GlideTime – 8/5/2015

Community Code Snippets - Events in a Single Picture – 8/30/2015

Community Code Snippets - Finding  a Record in an Object Array - 8/31/2016

Community Code Snippets: Getting the User's View - 8/27/2016

Community Code Snippets: The JavaScript Error Object in ServiceNow  - 11/3/2015

Community Code Snippets - Javascript Switch Statement – 7/19/2015

Community Code Snippets - Logging: Some Notes on Variable Substitution - 7/6/2016

Community Code Snippets - Passing by Reference vs. By Value – 9/2/2015

Community Code Snippets - Removing Duplicates From an Object Array - 8/18/2016

Community Code Snippets: Script Includes - So What Exactly is the Type For? - 7/16/2016

Community Code Snippets - Scripting Memory Management – 9/9/2015

Community Code Snippet: Server-Side Millisecond Time Logging - 4/2/2017

Community Code Snippets - Setting a Custom Image in Your KB Image Field – 9/16/2015

Community Code Snippets: Some Observations on parseInt, and parseFloat - 7/14/2016

Community Code Snippets: Some Observations on String Conversion Performance - 7/12/2016

Community Code Snippets - Suggested Scripting Resources – 8/3/2015

Community Code Snippets - System Log Ordering Problem Workaround – 8/20/2015

Community Code Snippets - Three Methods to Sort an Object Array - 8/19/2016

Community Code Snippets: Using a System Property to Control Logging - 8/28/2016

Community Code Snippets: Where Does All The Code Hide? - Revisited - 7/18/2016

Mini-Lab: Adding Underscore.js Into ServiceNow - 11/5/2015

Mini-Lab: Fix Script Application Enhancement – 10/1/2015

Mini-Lab - Events – 9/3/2015

Mini-Lab - Events: Passing Objects – 9/10/2015

Mini-Lab: Extending the JavaScript Array Object with findIndex - 8/30/2016

Mini-Lab: Extending ServiceNow JavaScript Using ECMAScript 6 Polyfills  – 10/13/2015

Mini-Lab: Using Ajax and JSON to Send Objects to the Server - 1/26/2016

Mini-Lab: Writing Entries Into the System Log Using Ajax and JSON - 1/28/2016

ServiceNow Scripting 101: Retrieving All Dictionary Entries in a Table Hierarchy - Originally 2/4/2015, re-publication: 5/25/2016

ServiceNow Scripting 101: Two Methods for Code Development (Fix Scripts vs. Scripts - Background) - Originally 8/6/2015, re-publication: 5/7/2016

Where Does All The Code Hide? - 9/14/2013

 

Scheduled Jobs

Mini-Lab: Creating a Scheduled Notification - 7/4/2016

 

Scoped Applications / Studio IDE

Ask the Expert w/ Steven Bell : Converting a UI Script Library from Global to Scoped - 2/2/2017 (video)

Ask the Expert With Steve Bell MVP, Scoped Application Ins and Outs Best Practices - 3/2/2017 (video)

Ask the Expert: Scoped Libraries with Steve Bell - 11/10/2016 (video)

Ask the Expert -  Scoped Debugging: Logging - Tips and Tricks with Steve Bell MVP - 4/11/2017

Community Code Snippets: Scoped Application Distribution - 4/4/2017

Community Code Snippets: Studio Ins-and-Outs - 4/6/2017

Mini-Lab: Converting a UI Script Library From Global to Scoped - Part 1 - 3/27/2017

Mini-Lab: Converting a UI Script Library From Global to Scoped - Part 2 - 3/29/2017

Mini-Lab: Re-pointing a Repository URL For the Studio IDE - 3/31/2017

Mini-Lab: Scoped Debugging and Logging – Part 1 - 4/17/2017

Mini-Lab: Scoped Debugging and Logging – Part 2 - 4/19/2017

Mini-Lab: Scoped Debugging and Logging – Part 3 - 4/21/2017

 

Service Catalog

Mini-Lab: Service Catalog - Building a Custom Choice Table  - 2/25/2016

ServiceNow Admin 101: Creating a “Top of Page” Link for Service Catalog Pages - Originally 6/10/2014, re-publication: 5/29/2016

 

Standards

Community Code Snippets:  Code Formatting – 10/29/2015

Community Code Snippets: Variable and Function Naming – 10/22/2015

Pragmatic Patterns: Ajax - Architecting Your Code - 1/24/2016

Pragmatic Patterns: Workflows - Using Data to Adjust the Flow - 2/3/2016

 

Update Sets

Community Code Snippets:  A Tip For Vetting Update Sets  - 11/17/2015

Mini-Lab: Moving Update Set Customer Update Records - 11/19/2015

 

User Interface

Mini-Lab: Add a Slush Bucket Control to a Dialog Box - 9/8/2016

ServiceNow Admin 101: How to Override the UI14 “Dot” in a List View Using CSS Styles - Originally published 12/30/2014, re-publication: 5/27/2016

 

Web Services

Mini-Lab: Adding Filters to the C#/.NET WebService Example - 1/6/2016

Mini-Lab: Retrieving and Parsing the JSON Results From a REST Web Service - 9/6/2016

Mini-Lab: Testing REST With SoapUI - 11/30/2015

Mini-Lab: Visual Studio Community 2015 and the C# Web Service Example - 12/31/2015

Mini-Lab: Web Services – Part 1: Using SoapUI to Test ServiceNow WSDL  - 11/25/2015

Mini-Lab: Web Services – Part 2: Using an Encoded Query - 11/24/2015

Mini-Lab: Web Services – Part 3: Writing a Simple Scripted Web Service - 11/23/2015

Mini-Lab: Web Services – Part 4: Modifying Help-the-Help-Desk to use a scripted Web Service - 11/29/2015

Parsing the JSON Result from a Web Service - Ask the Expert Steven Bell - 10/13/2016 (video)


Workflows

Ask the Expert : Workflow Demonstrations in Helsinki with Steven Bell MVP - 6/8/2016 (video)

Community Code Snippets - Logging: Some Notes on Workflows - 7/10/2016

Community Code Snippets - Logging: Some Notes on Workflows – 11/1/2015

Mini-Lab: Sending a JSON Object to a Workflow - 9/4/2016

Mini-Lab: Workflows - Add Users to Groups - Service Catalog/Workflow - 7/2/2016

Mini-Lab: Workflows – Branch and Join Example – 9/20/2015

Mini-Lab: Workflows – For Loop  – 9/28/2015

Mini-Lab: Workflows - Playing With the If Activity  – 10/18/2015

Mini-Lab: Workflows – Turnstile Enhancement  – 9/29/2015

Mini-Lab: Workflow Control Via Property Input  - 2/13/2016

Mini-Lab: Workflow Control Via Variable Examination (Advanced)  - 2/18/2016

Mini-Lab: Workflow Control Via Variable Input - 2/6/2016

 

My Old Blog Articles (2013)

Welcome! - 7/24/2013

Fun With Queries - 7/25/2013

Our First Company Hackathon - 7/30/2013

Development Process in ServiceNow - 9/13/2013

Where Does All The Code Hide? - 9/14/2013

Experiences Implementing ServiceNow Discovery (Part 1 of 2) - 10/7/2013

Experiences Implementing ServiceNow Discovery (Part 2 of 2) - 10/10/2013

 

 

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!

 

Combined Logo Graphic_Operations.png

Coming from a formal Computer Science and engineering background it is sometimes painful to see certain naming conventions utilized.  For example:  One-, two-, or three letter variable names.

 

 

There is an old school-of-programming thought:  If it was hard to write it ought to be hard to read!

 

I have never believed in that adage!

 

Did you know there were several very good JavaScript naming conventions already in use?  JavaScript has been around for quite awhile (link).  And standards for the language as well (link).  Most large JavaScript development shops use Java coding conventions for their JavaScript standards.  A significant number of people have created a coding standard in one form or another. The best I have found to-date are:

 

JavaScript Naming

from the document: JavaScript Guide

 

Douglas Crockford in his book "JavaScript: The Good Parts" uses single letter variables all over the place.  I do not support this convention (However, in his defence he "might" be doing it for brevity in the book.  :-/ ).

 

ServiceNow supports meaningful names for variables and functions:

Coding Best Practices: Use Descriptive Variable and Function Names

 

Cloud Sherpas' own tony.fugere wrote on ServiceNow scripting standards two years ago focussing a part of his article on naming standards:

ServiceNow Admin 101: Coding Standards

 

 

So here are my thoughts on this topic:

 

Name your variable and functions something meaningful. var gr is not meaningful, nor is it descriptive enough. Consider using naming conventions that define what’s stored or will be stored in the variable.

 

NOTE: the variable name "gr" is quite commonly used to represent GlideRecord throughout the codebase.  Try to avoid using this convention as it is not descriptive of what is actually happening.  Consider, would you name an integer: var int = 5; ?

 

You get the idea.  Something meaningful.  Don't do naming like this:

 

     var gr = new GlideRecord('incident');

 

     var il = [];  //incident list

 

     var m = {};

 

     function x(y) {...}

 

Each of these can be hard to maintain (even with comments)!  I mean, seriously...what does the function name "x" signify?

 

The one exception I make to this is on so-called throw-away variables such as are used in loops.  For example:

 

     for (var i=0; i < incidentList.length; i++) {

          gs.print(incidentList[i].value);

     }

 

The function of such a throw-away variable is obvious, and making it longer may add to confusion rather than helping with readability.  BTW, I once knew a company architect that insisted that such variables not be used, and forced every coder to use "meaningful" names for counters.  I am of the opinion that this school of thought is tedious and unproductive!  I am almost on the hate level with things like this:

 

     for (var incidentListCount=0; incidentListCount < incidentList.length; incidentListCount++) {

          gs.print(incidentList[incidentListCount].value);

     }

 

Yuck (a professional term I use for meaning yuck!).

 

Also, stay away from non-descriptive naming of variables and functions such as these:

 

     var myObj = {};

 

     function myFunction(things) {...}

 

Such names don't really state what the variable or function is for!

 

It is okay, and preferable, to use long descriptive names. This is known in some developer circles as self-documenting code.  Look at these examples and you will see what I mean:

 

     var incidentRecords = new GlideRecord('incident');

 

     var incidentList = [];  // list of incident objects

 

     var incident = {};

 

     var incident_results = new getIncidentResults();

 

     function calculateAssetValue(assetToEvaluate) {...}

 

Camelcase is fine, as is Snakecase (underscores).  Both are considered standards in the ServiceNow codebase.  This kind of naming is very maintainable, and may reduce the amount of comments needed.  As you can see adding an extra (short) comment next to the variable declaration can be useful in some cases.

 

Don't go crazy with this though.  While long names are fine, overly long names are ridiculous and can actually contribute to maintenance issues by introducing unreadability!

 

     var thisReally_long_variableNameIsNotAGoodIdea = 5;

     var iWouldNotRecommendTooLongVariableNames += thisReally_long_variableNameIsNotAGoodIdea;

     var myArray = goGetAnIncidentListAndVerifyItTurnItIntoAnArrayAndReturnIt(iWouldNotRecommendTooLongVariableNames);

 

You get the idea, and yes, I have seen stuff like this in the codebase out there! :-/

 

Another thing I would suggest avoiding are two variables or functions that are named similarly.  You are asking for errors to be introduced later if a maintenance coder uses the wrong one!

 

     var incidentList = [];

     var incidentsList = [];

 

And yeah, seen that a lot as well!

 

So in conclusion consider using sensible names for your variables and functions.  Something descriptive, longer than four characters, and that to some degree self-documents.  This improves readability, maintainability, and documentation of the code.

 

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!

There are several ways of limiting records to be displayed to the user.  Of these there are three out-of-the-box (OOtB) that are most widely used:

 

    ACLs

    Data Policy

    Query Business Rule

 

Of the above I like the Query Business Rule (BR) as it gives a seamless user experience. With this article we will be exploring the Query BR, and how you can limit user access to data.

 

In this example I will give a couple of best practices:

 

    Requirements and Design gathering

    Role to Group Association

    Using a Query BR to Limit Data exposure

 

Requirements: 

 

Our fictitious company Wigit-Inators would like to constrain all user access to widget Incidents.  The desire is to limit these records to be viewed by only authorized personnel.  This Widget Analyst would be allowed to see all incident records including Widget incident records.  These records will also be those that include the word Wire (just to give us more to see).  All other Itil users would be excluded from seeing Widget and Wire Incidents

 

1. Create a new Role: Widget Analyst

2. Limit access to widget records in the Incident table to be viewable only by those with the Widget Analyst role.  Keep the other Itil user(s) from seeing Widget Incident records.

3. Only records with the word "wire" or "widget" will be affected.

4. ServiceNow admins will also have access

 

Design:

 

1. Create new role: widget_analyst

2. Create new group: Widget Analysts

3. Create new Query BR:  Widget Exclude that:

    a. Will allow only analysts with the widget_analyst role to see widget records.

    b. The widget records will not be visible to those without the widget_analyst role.

    c. Admins will be allowed to see widget records as well.

    d. Widget Analysts will be able to see all other Incidents as well as widget records.

    e. Widget Analysts and Admins will be able to see Incidents with the words "wire" or "widget in the short_description field.  All other Itil users will not be able to see these incident records.

 

NOTE: Best Practice: Always assign roles to a group, then assign users to the group.  Never assign roles directly to users.  With roles it is much easier to maintain a group than individual users. 

 

 

Lab 1.1 – Limiting Data Access With a Query BR

 

1. Create new role

    a. Navigate to User Administration -> Roles. The Roles list view will be displayed.

    b. Click on the New button.  The New Role form will be displayed.

    c. Fill in the form with the following:

        i.   Name: widget_analyst

        ii.  Description: Persons who analyzes widgets

        iii. Click the Submit button to save the new role.

 

x.widget_analyst role.png

 

2. Create new group

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

    b. Click on the New button.  The New Group form will be displayed.

    c. Fill in the form with the following:

        i.   Name: Widget Analysts

        ii.  Description: People with the Widget Analyst role

    d. Right-click on the form header to bring up the form context menu. Click on the Save option to save your work.

    e. Scroll to the bottom of the Group form and choose the Roles tab.

        i.   Click the Edit button.  The Edit Members form will be displayed.

        ii.  Choose the Itil role.  The users will need full access to all incidents.

        iii. Choose the widget_analyst role.  These users will be able to view the widget incident records.

        iv.  Click the Save button to save your work and return to the Group form.

        v.   You will see information messages displayed at the top of the form notifying you of the role assignments to the group.

 

x.widget analyst group.png

 

    f. Choose the Group Members tab.

        i.   Click the Edit button.  The Edit Members form will be displayed.

        ii.  Choose your favorite user(s) that you want to be widget people.

        iii. Click the Save button to save your work and return to the Group form.

        iv.  You will see information messages displayed at the top of the form notifying you of the role assignments now given to the users assigned to the group.

 

x.user assignment.png

 

3. Create new Query BR

    a. Navigate to System Definition -> Business Rules.  The Business Rules list view will be displayed.

    b. Click on the New button.  The New Business Rule form will be displayed.

   c. Fill in the form with the following:

        i.   Name: Widget Exclude

        ii.  Table: Incident

        iii.  Active: Checked

        iv.  Advanced: Checked

    d. Click on the When To Run tab

        i.   When: before

        ii.  Order: 100

        iii. Query: checked

 

x.display br.png

 

   e. Click on the Advanced tab

        i.   Condition: None.

        ii.  Script

 

function onBefore(current, previous) {
    // Look for these words in the short descript and use it as a filter
    // This script adds to the overall list view query that is used to display
    // the list view
   if (gs.hasRole("widget_analyst") || gs.hasRole("admin")) {
        var incidentInclusion = current.addOrCondition('short_description', 'CONTAINS', 'widget')
            .addOrCondition('short_description', 'CONTAINS', 'wire');
   }
    else {
        var incidentExclusion = current.addQuery('short_description', 'DOES NOT CONTAIN', 'widget')
            .addQuery('short_description', 'DOES NOT CONTAIN', 'wire');
    }
}
    

 

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

 

x.display br advanced tab.png

 

We are done!  Now let's test our new BR!

 

 

Lab 1.2 – Testing the Business Rule

 

NOTE: For more information on impersonating a user see this wiki article which is a bit dated, but conveys the idea.

 

1. Navigate to Incident -> Open.  This will display a list view of open Incidents.

    a. Edit the description field of two or three of these to include the word "widget", or "wire".  These will be picked up by our new BR.

 

2. Navigate to User Administration -> Roles.  The display view of roles will appear.

    a. Filter for the Itil role

    b. Click on the Itil role

    c. Click on the Users tab

    d. Observe a user who is not in your Widget Analyst group.

 

3. Impersonate one of the users in your Widget Analyst group and test for access.

    a. Click on the impersonate button.

    b. Pick your user.

x.impersonate allowed user.png

    c. Navigate to Incident -> Open

    d. If the Short Description field is not visible on the list view then personalize your list view to add it.

    e. Filter on all records with the words "widget", or "wire".

    f. Expected result: You should see these records in the list view.

 

x.user in group.png

 

3. Impersonate one of the Itil users not in your Widget Analyst group and test for access.

     a. Click on the impersonate button.

     b. Pick your user.

     c. Navigate to Incident -> Open

     d. Filter on all records with the words "widget", or "wire".

     e. Expected result: No records should be displayed.

 

x.user not in group.png

 

4. Change back to the Administrator and test for access.

     a. Click on the impersonate button.

     b. Pick the Administrator user.

     c. Navigate to Incident -> Open

     d. Filter on all records with the words "widget", or "wire".

     e. Expected result: You should see these records in the list view.

 

x.admin user.png

 

And our testing is completed!  Wigit-Inators is ecstatic over the new functionality you have provided!  :-)

 

As you can see the user without access will not even be aware that records are being filtered.  There is absolutely no indication on the list view.  I really like that feature of Query BRs!

 

BTW, with a little thought you could refactor the BR script to be the following:

 

function onBefore(current, previous) {
    // Look for these words in the short descript and use it as a filter
    // This script adds to the overall list view query that is used to display
   // the list view
    if (!gs.hasRole("widget_analyst") && !gs.hasRole("admin")) {
          var incidentExclusion = current.addQuery('short_description', 'DOES NOT CONTAIN', 'widget')
              .addQuery('short_description', 'DOES NOT CONTAIN', 'wire');
    }
}
    

 

I will leave you to do the testing!  :-)

 

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!

In this example we will be exploring the Advanced functionality available with the out-of-the-box (OOtB) If Activity.


If you have ever tried to use the Switch Activity you will notice that it does not take a scratchpad variable (sic. workflow.scratchpad).  This shortcoming can be overcome by using a little-known feature of the If Activity: the Advanced checkbox.  When checked this displays the Script field where you can create code using a JavaScript switch or if/then/else if/else coding structure. Another Activity function that most people are unaware of is the Activity Context menu.  For the If Activity this allows for the addition of more conditions.  These two features will be used in this example to give us the capability of  programmable Switch Activity.


NOTE: This example assumes a certain familiarity with ServiceNow Workflows and using the Workflow Editor.

 

NOTE: For this example I will be using an if/then/else if/else code structure, but I could have just as easily used a switch.


Lab 1.1 – Using an Advanced If Activity


1. In your ServiceNow instance Navigate to Workflow Workflow Editor to bring up the Workflow Editor form.

 

2. Click the "+" to create a new workflow.  This will display the New Workflow form.  Fill in the form with the following:

  a. Name: Playing With the If Activity

  b. Table: Global

  c. Click the submit button to create the new workflow.

 

8.sub-workflow_add workflow.JPG

 

3. From the Core Activities tab navigate to Utilities and drag out a Run Script activity

    a. Name: Initialize

    b. Script:

 

// This variable will determine which path the If Activity will take
workflow.scratchpad.trigger = 'red';
//workflow.scratchpad.trigger = 'blue';
//workflow.scratchpad.trigger = 'green';
//workflow.scratchpad.trigger = 'orange'; // fires the none branch


// This line will cause two paths to fire simultaneously
//workflow.scratchpad.trigger = 'red | green';

 

    c. Click the Submit button to save your work.


    NOTE: The commented out code represents the various test conditions we will be applying to our finished workflow.

 

4. From the Core Activities Tab navigate to Conditions and drag out an If activity.

    a. Name: Validate Trigger

    b. Advanced: Checked

    c. Script:

 

workflow.scratchpad.result = ifScript();

function ifScript() {
    var result = {red:false, blue:false, green:false, none:false};

    if (JSUtil.notNil(workflow.scratchpad.trigger)) {
        if (workflow.scratchpad.trigger.indexOf('red') > -1) {
            result.red = true;
        }
        if (workflow.scratchpad.trigger.indexOf('blue') > -1) {
            result.blue = true;
        }
        if (workflow.scratchpad.trigger.indexOf('green') > -1) {
            result.green = true;
        }
    }

    if (!(result.red || result.blue || result.green)) {
        result.none = true;
    }

    //gs.log('---> ' + result.red + ',' + result.blue + ',' + result.green + ',' + result.none, 
    //   'Playing With the If Activity.Validate Trigger');
    return result;
}


    d. Click the submit button to save your work.

 

x.Advanced Checkbox.png


5. From the Workflow double-click on the If Activity "Yes" Condition. This will display the Condition form.  Change the condition to the following:

    a. Name: is Red

    b. Skip During Generate: checked

    c. Condition: workflow.scratchpad.result.red == true

   d. Click the Update button to save your work.

 

6. From the Workflow double-click on the If Activity "No" Condition. This will display the Condition form.  Change the condition to the following:

    a. Name: is Blue

    b. Skip During Generate: checked

    c. Condition: workflow.scratchpad.result.blue == true

    d. Click the Update button to save your work.

 

7. Right click on the If activity and choose Add Condition.  This will display a blank Condition form.  Fill in the form with the following:


    NOTE: Be patient when adding a new condition.  For some reason it takes the browser several seconds to render the context menu.


    a. Name: is Green

    b. Skip During Generate: checked

    c. Condition: workflow.scratchpad.result.green == true

    d. Click the Update button to save your work.

 

8. Right click on the If activity again and choose Add Condition.  This will display a blank Condition form.  Fill in the form with the following:

  a. Name: is None

    b. Skip During Generate: checked

    c. Condition: workflow.scratchpad.result.none == true

    d. Click the Update button to save your work.

 

x.Add Condition.jpg

 

9. Wire up the workflow to look something like the following:

 

3.final workflow.png

 

Done!  We are now ready to test the new If activity!

 

 

Lab 1.2 – Testing the Workflow


 

1. Click the Play button in the upper right of the Workflow editor screen to run our If Activity workflow.


 

2. Since we are manipulating the If activity with a variable in the Initialize Run Script activity the first branch of the If activity (Red) will trigger.

 

3. The resultant context should look like this:

 

x.Red run Context.png

4. Now edit the Run Script activity

    a. Comment out the "red" line.

    b. Uncomment the "blue" line.

    c. Click the submit button to save your work.

    d. Click the Run Workflow button and observe the "blue" branch is triggered.

 

x.Blue run Context.png


5. Edit the Run Script activity

    a. Comment out the "blue" line.

    b. Uncomment the "orange" line.

    c. Click the submit button to save your work.

    d. Click the Run Workflow button and observe the "none" branch is triggered.

 

x.Orange run Context.png


6. Edit the Run Script activity

    a. Comment out the "green" line.

    b. Uncomment the "red | green" line.

    c. Click the submit button to save your work.

    d. Click the Run Workflow button and observe both the "red" and the "green" branches are triggered.  This will demonstrate the firing of two branches simultaneously.


x.Red and Green Context.png

 

Now we are done testing.  That is all there is to it!  Cool, huh?! 


BTW, a large number of OOtB activities have scripting capability which allows for flexibility to adapt to a variety of needs.

 

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 also like to encourage you to become a member of our blog!

ServiceNow's mobile UI gives you convenient remote access to your instances with some standard functionality that lets you perform common tasks while on the go. We support two types of smartphone interfaces:

  • Smartphone interface – This is active by default on new instances starting with the Dublin release. (For existing instances that are upgraded to Dublin or later, admin can activate the smartphone interface.)
  • Legacy mobile UI - These are devices, browsers, and features supported in versions prior to the Dublin release. For more information, see Configuring the Legacy Mobile UI.

 

In addition, administrators can define what users can access on smartphones through the smartphone interface. For example, users can be given access to specific application menus and modules, default home page favorites, UI policies, and online help.

 

In the Incident module, for example, the + button appears on the top-right corner of the screen in a list on the mobile UI. Pressing this button creates a new record in the list.

 

Screen Shot 2015-08-29 at 09.58.11.JPG

In some cases, you may want to restrict certain users from creating new incident (or other types of records) from the mobile UI but still allow them to create new records from the desktop UI.

 

The "+" on the mobile UI essentially means, "create a new record from the type of the list you are in". Because this button is hard-coded in the UI and not one of the UI actions, you're not able to create conditions directly on the button. Instead, you'll need to create an ACL to restrict the mobile UI.

 

To control the + button and create an ACL to restrict the mobile UI:

  1. If needed, elevate privileges to the security_admin role.
    Elevate_New.jpg
  2. Navigate to System Security > Access Control (ACL).
  3. Create an access control using the following information:
    • Type: Record
    • Operation: Create
    • Name:  Use the name of the table you want to restrict, for example, Incident.
    • Active: Checked
    • Advanced: Checked
      Limit_Access_Mobile_UI.jpg
  4. Enter the following code in the Script field: GlideTransaction.get().getPageName() != "angular”;
  5. Click Submit.

The script in step 4 obtains the name of the page and checks to see if it's "angular" (name of the page in the mobile UI). When it checks the name, one of the following occurs:

  • If it's not angular, then it's not mobile, and the ACL passes.
  • If it's angular, then it's in mobile, and the ACL restricts access so the + button does not appear.

 

Note: While this does function, it is not an official solution. The name used for the mobile page may change in future versions. If this fix is implemented, be sure to test after upgrades to ensure you are seeing the expected behavior.

 

 

Looking for more tips and solutions for mobile UI?

 

We have an entire resource page for this! See Mobile User Interface Resources for more information.

Awhile back I wrote an article on how to UNION two record sets. Since I am asked about this often in my ServiceNow Scripting classes; I thought I would publish the link here.

 

There are a couple of approaches to implementing this.  If you want a resultant recordset to report against then you will have to do it with Database Views.  If you want a coding solution to merge recordsets from two different tables then you will need to take the approach I outlined in my article.

 

ServiceNow Admin 101: How to Do a UNION Query Using GlideRecord

 

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!

CS_logo.jpgMVP-logo.jpeg

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

 


for Click for More Expert Blogs and also Find Expert Events!


I stumbled across a new Fuji feature a while back around script includes and ajax calls that I thought was pretty handy. I haven’t seen much about it on the community so I figured I’d call it out here.


If you’ve ever used GlideAjax in ServiceNow to run some server side code from a client script you know that there are two parts to the process. You need to initialize GlideAjax from the client side, most likely from a client script, and then you also need a script include on the server side where you’ll run your server side code.


If you’re like me you probably start by copying the client side code from the wiki article and changing the parameter names, and then you do the same with the server side code and change the function names. In Fuji this has gotten a little easier.


If you’re creating a standard script include to be used server side, the functionality has been around a while where ServiceNow will fill in some starter code after you name your script include.


6acffb2afd.png


The problem with doing this for a script include that needs to be called from the client is that you need to extend the AbstractAjaxProcessor object and you don’t want to override the initialize function. Well, in Fuji at this point you can check the Client callable checkbox and voila!


ed7b32596d.png


The nice thing about this is you don’t have to do anything differently than you normally would, and it saves you the time of copying and pasting from the wiki article.


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


Martin Barclay

Developer Day Debut

Posted by Martin Barclay Employee Oct 13, 2015

excel_london.jpg

 

Today at ExCel London and the day before NowForum London, ServiceNow Platform Business Unit and EMEA sales & marketing teams welcome nearly 400 developers from ServiceNow customers and partners to a full day of sessions, live coding, and demos of all the latest things app development, integration, and deployment on the ServiceNow Platform.

 

This is the first-of-it's kind ServiceNow Developer Day modeled after CreatorCon - the first of many I expect based on the sold-out status of this one.

 

10 subject matter experts from the Platform Business Unit, led by Pat Casey, will articulate the Platform vision and strategy and cover top app dev and integration topics over a total of 12 sessions. As a special bonus, Fred Luddy will be on stage for a Q&A with Pat Casey.And there will be an Expo area with 6 demo pods showing Encryption, Advanced Developer Tools, Integrations, App Model, ServiceNow Store and Developer Program - a great opportunity for attendees to get up close with the latest in developer/ISV products and concepts, interact with the experts,and advance their professional expertise developing on the ServiceNow Platform.

 

I'm excited to meet with the ServiceNow developer community here in London and am looking forward to an awesome day. Cheers.

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

Building off of two of my recent articles (Extending GlideRecord, and Advanced Object Filter) I will show you how you can use MDN ECMAScript 6 Polyfill definitions to extend JavaScript in ServiceNow.

 

So, what does this mean exactly.  Well with JavaScript it is possible to define future functionality using what are called Polyfills.  A Polyfill implementation is simply a prototype that implements the new functionality.  Now using the method I described in Extending GlideRecord you can also extend the base JavaScript language!

 

Here is how.

 

Lab 1.1: Finding and Using the ECMAScript 6 Polyfills

 

1. Here is the location of a few useful polyfills:

 

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

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

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

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

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


a. Find the Polyfill in the referenced article. 

b. Copy the entire Polyfill script out of the article and paste it into your new Script Include (described in the next step).

 

2.polyfill location.png

2. We can copy this code out pretty much without modification into a single Script Include.

    a. Navigate to System Definition -> Script Includes.

    b. Click on the New button to create a new Script Include

        i.   Name: JavaScriptExtensions

        ii.  Accessible from: All application Scopes

        iii. Client callable: Checked

        iv. Description: Add ECMAScript6 functionality to the ServiceNow JavaScript library


v. Script:  Add the code exactly as you find it in each of the Polyfill locations at the referenced pages.  Place them one after the other in your Script Include.  With these we are creating a language extension library of functions.  You code should look a bit like this:

  1.js extension code.png

 

NOTE: I have added this code out onto the share here.

 

       vi. Click the Submit button to save the new Script Include.


NOTE: All five Polyfills can be added to the same Script Include to form a function library.

 

3. Once we save this we can "include" this Script Include in any client or server-side script of our choosing.


 

 

Lab 1.2: Testing the JavaScriptExtensions functionality

 

NOTE: To fully test using this lab; all five of the Polyfills mentioned in Lab 1.1 must be present in the new Script Include.


1. Navigate to System Definition -> Fix Scripts.  The Fix Scripts List View will be displayed.

 

2. Click on the New button.  A new Fix Script form will be displayed.

    a.  Name: JavaScript Extension Test

    b.  Active: checked

    c.  Description: This Fix Script is for testing the JavaScript ECMAScript 6 extensions.

    d.  Script:

 

gs.include('JavaScriptExtensions');

// --- String.repeat extenstion
gs.print('----------- String repeat Extensions');
//'abc'.repeat(-1);   // RangeError
gs.print('abc'.repeat(0));    // ''
gs.print('abc'.repeat(1));    // 'abc'
gs.print('abc'.repeat(2));    // 'abcabc'
gs.print('abc'.repeat(3.5));  // 'abcabcabc' (count will be converted to integer)
var depth = 5;
gs.print('-'.repeat(4 * depth));
//'abc'.repeat(1/0);  // RangeError

var quote = 'To be, or not to be, that is the question.';

gs.print('\n----------- String startsWith Extensions');
// ---- String.startsWith extension
gs.print(quote.startsWith('To be'));         // true
gs.print(quote.startsWith('not to be'));     // false
gs.print(quote.startsWith('not to be', 10)); // true

gs.print('\n----------- String includes Extensions');
// ---- String.includes extension
gs.print(quote.includes('To be'));       // true
gs.print(quote.includes('question'));    // true
gs.print(quote.includes('nonexistent')); // false
gs.print(quote.includes('To be', 1));    // false
gs.print(quote.includes('TO BE'));       // false

gs.print('\n----------- String endsWith Extensions');
// ---- String.endsWith extension
gs.print(quote.endsWith('question.')); // true
gs.print(quote.endsWith('to be'));     // false
gs.print(quote.endsWith('to be', 19)); // true

// ---- test the Array.fill extension
gs.print('\n----------- Array fill Extensions');
gs.print([1, 2, 3].fill(4));               // [4, 4, 4]
gs.print([1, 2, 3].fill(4, 1));            // [1, 4, 4]
gs.print([1, 2, 3].fill(4, 1, 2));         // [1, 4, 3]
gs.print([1, 2, 3].fill(4, 1, 1));         // [1, 2, 3]
gs.print([1, 2, 3].fill(4, -3, -2));       // [4, 2, 3]
gs.print([1, 2, 3].fill(4, NaN, NaN));     // [1, 2, 3]
gs.print(Array(3).fill(4));                // [4, 4, 4]
var checkArray = [].fill.call({ length: 3 }, 4);  // {0: 4, 1: 4, 2: 4, length: 3}
for (var i=0; i < checkArray.length; i++) {
    gs.print(i + ': ' + checkArray[i]);
}


 

    e. Right-click on the form header to bring up the context menu and choose Save to save your work.


4. If you have not enhanced your Fix Scripts form scroll down to the bottom of the form and click on the Run Fix Script related link to run the script.

 

5. Your results should look something like the following:


4.run.png

 

I have posted the latest JavaScriptExtensions code out on the share with this functionality if you would like to download it.

 

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!

NOTE: This article builds off of my previous GlideRecord extension article.

 

I have wanted SQL BETWEEN capability for some time with GlideRecord.  With the ability to extend the out-of-the-box object myself it then became a relative snap to implement this feature (albeit not quite the perfect way I would want, but good enough).

 

Requirements:

 

1. Extends the GlideRecord object as a new .addQuery type.

2. Takes two GlideDateTime objects as a from-date, and to-date.

3. Returns all records for any query between the given range for any given GlideDateTime field.

 

SQL Example:

 

SELECT * FROM incident WHERE sys_created_on BETWEEN '2013-06-12' AND '2014-11-28'

 

Desired GlideRecord Implementation:

 

<<GlideRecord>>.addBetweenQuery(<<String>>field, <<GlideDateTime>>fromDate, <<GlideDateTime>>toDate);

 

NOTE: My actual desired implementation would have been something like this:

 

<<GlideRecord>>.addQuery(<<String>>field, 'BETWEEN', <<GlideDateTime>>fromDate, <<GlideDateTime>>toDate);

 

But currently I don't want to have to sort through the partial overriding of .addQuery it would take.  Maybe later!  :-)

 

 

Lab 1.1: Extend GlideRecord With New Between Function

 

NOTE: If you have already created the GlideRecordExtensions Script include from my other article you can skip steps 1 through 3.

 

1. Navigate to System Definition -> Script Include.  The Script Include List View will be displayed.

 

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

 

3. Fill in the following fields:

    a. Name: GlideRecordExtensions

    b. Accessable From: All Application Scopes

   c. Active: checked

   d. Right-click on the form header to bring up the context menu and choose Save to save your work.

 

 

4. Now continue filling in the remaining fields:

 

    a. Description:

        addBetweenQuery - search for all records between two dates inclusive

 

    b. Script:

 

  GlideRecord.prototype.addBetweenQuery = function(fieldToCheck, beginDate, endDate) {

      var beginDateCheck = gs.dateGenerate(beginDate); // convert to the appropriate string
      var endDateCheck = gs.dateGenerate(endDate); // ibid.

      this.addQuery(fieldToCheck, '>=', beginDateCheck);
      this.addQuery(fieldToCheck, '<=', endDateCheck);
  };
  

 

    c. Click on the Update button to save your work.

 

1.Script Include.jpg

 

 

Lab 1.2: Testing the .addBetweenQuery functionality

 

1. Navigate to System Definition -> Fix Scripts.  The Fix Scripts List View will be displayed.

 

2. Click on the New button.  A new Fix Script form will be displayed.

 

3. Fill in the following fields:

    a. Name: GlideRecord Extension Between Test

    b. Active: checked

    c. Run Once: true

    d. Script:

 

gs.include('GlideRecordExtensions');

var beginDate = new GlideDateTime('2013-06-12');
var endDate = new GlideDateTime('2014-11-28');

// Retrieve all active records between the two dates inclusive!
var incidents = new GlideRecord('incident');
incidents.addBetweenQuery('sys_created_on', beginDate, endDate); //<--- Our new function!
incidents.addQuery('active', true);
incidents.query();

gs.print('---> Records found: ' + incidents.getRowCount());

// Convert our GlideRecord recordset to an object list for easier manipulation
var convertedList = incidents.toObjectList();
gs.print('---> Records converted: ' + convertedList.length);

for (var i=0; i < convertedList.length; i++) {
    gs.print(convertedList[i].number + ' - ' + convertedList[i].sys_created_on);
}

 

    e. Right-click on the form header to bring up the context menu and choose Save to save your work.

 

 

4. If you have not enhanced your Fix Scripts form scroll down to the bottom of the form and click on the Run Fix Script related link to run the script; click the Ok button on the next form, and the Proceed button on the next form (too many "are-you-sure-forms"!).

 

5. Your results should look something like the following:

 

results.png

 

BTW, you could derive this same kind of functionality by using the following built-in functionality .addEncodedQuery.  It would look like this:

 

incidents.addEncodedQuery("active=true^sys_created_onBETWEENjavascript:gs.dateGenerate('2013-06-12','00:00:00')@javascript:gs.dateGenerate('2014-11-28','00:00:00')");

 

The encoded query would be difficult to read, and difficult to maintain.  The addBetweenQuery is much easier to read!

 

My solution does not zero out the times, and you may want to implement that feature.

 

I have posted the latest GlideRecordExtensions out on the share with this functionality if you would like to download it.

 

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!

CS_logo.jpgMVP-logo.jpeg

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

 


for Click for More Expert Blogs and also Find Expert Events!


So, building off my last lab article where we extended the GlideRecord object I thought I would take the opportunity to demonstrate a slick method for searching an object array for a specific value.

 

ServiceNow uses the JavaScript standard JavaScript 1.5 - ECMAScript 3.1.5 (or some of it).  I have found elements of ECMAScript 5 available as well. 


One of these is the Array.filter method.  This is a Lambda like function that allows a developer to create a search function, pass that and a value to match against.  filter will then utilize the passed search function to find the value in the given array of objects, and return all of the objects that were located.  This is then passed back as an array of objects; a sub-set of the original.


The following code represents a way of using the Array.filter method (I also thew-in the use of my GlideRecord extension .toObjectList to help facilitate converting the Incident recordset into an object array):

 

// bring in our GlideRecord extension functions
gs.include('GlideRecordExtensions');

// get all of the active incident records
var incidents = new GlideRecord('incident');
incidents.addActiveQuery();
incidents.query();

gs.print(incidents.getRowCount());

// this will be the value that we will search for
var match = 'INC0000017';
var incidentList = incidents.toObjectList(); // convert to object array
var filteredList = incidentList.filter(findIt, match); // now find it!

// print off the number of everything we found (we should only have one match)
for (var i = 0; i < filteredList.length; i++) {
       gs.print(filteredList[i].number);
}

// this particular function looks at the incident.number field
// the "this" variable is our "match" parameter
function findIt(value) {
      if (value.number == this) {
            return value;
      }
}




NOTE: This example can be run in either Fix Script or Scripts - Background, and requires the installation of the Gliderecord extension file on your instance.


NOTE: To pass in more than one parameter for the search you create an object and pass that in.  The object would contain multiple properties that would represent other match criteria.


If you are interested; the next version of this you can find it here ECMAScript 6; where we will actually see a Lambda function!  It makes this process even better by refining how the function is passed. 

 

You can find out more about what the "filter" method provides here:


Mozilla

Microsoft


If you want to know more about the JavaScript language versions see: Wikipedia


Steven Bell


P.S. Yes, yes, mamann I probably should have put this example into an anonymous function.  :-p

 

 

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

Lets talk about:

  • Watermarks
  • Two watermarks on the incoming emails
  • Testing two watermarks on the incoming emails

 

Watermarks

 

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.

 

Inbound email actions might not work properly if watermarks are omitted from email notifications.

 

Two watermarks on the incoming emails

 

Sometimes, inbound actions can have more than one watermark added by mistake. It could be caused by comments appended from other incoming records, text matching Ref:, etc.

If two watermarks are found on an email, the incoming email will use the first watermark, ignoring the second.

 

To resolve this problem, you need to ensure the first watermark is the one wanted or add it to the subject of the incoming email.

 

dani-pedrosa-marc-marquez-repsol-honda-rc213v1.jpeg

 

 

Testing two watermarks on an incoming emails

 

I’ve sent to my instance 8 emails based on 4 existing watermarks:

  • OK1: Watermark 1: Ref:MSG0000330 (valid) - Incident: INC0000055
  • OK2: Watermark 2: Ref:MSG0000331 (valid) - Incident: INC0010009
  • NO1: Watermark 3: Ref:MSG0XXXXX1 (invalid)
  • NO2: Watermark 4: Ref:MSG0XXXXX2 (invalid)

 

watermarks.png

 

Here is the watermark matching result of incoming emails with two watermarks:

 

Cases

Email 1

Email 2

Email 3

Email 4

Email 5

Email 6

Email 7

Email 8

Subject

-

-

-

-

OK1

OK1

NO1

NO1

Body

OK1

OK1

NO1

NO1

OK2

NO1

OK2

NO2

Including watermarks

OK2

NO1

OK2

NO2

Result

OK1

OK1

No match

No match

OK1

OK1

OK2

No match

 

As you can see, it will search on the subject, then the first watermark on the body even if the second is valid.

Also notice that even if the subject watermark is invalid, we later try to find it on the body.

 

 

watermarks1.png

watermarks2.png

NOTE: Please note the syslog table hold the information on troubleshooting on watermark matching.

Search with <instance>/syslog_list.do?sysparm_query=sourceLIKE<email-sys-id>

 

Conclusion

You need to ensure the first watermark is the one wanted or add the watermark to the subject of the incoming email.

The email process will search on the subject, then the first watermark on the body.

 

Review what you have learn

 

After reading this blog you should be able to understand

  • How valid watermarks can be ignored
  • Watermarks on the subject
  • Where in the logs you can see the watermark matching to a target record.

 

More information here:

 

Note: Tested on Fuji, with Chrome

Since Fuji, administrators can now add indexes to their own instances as needed. Fellow support agent billt wrote in detail how you can Improve your ServiceNow instance performance by creating database indexes via the User Interface. To expand on this function, I want to focus on what you can do if you have a slow query in the slow query log and want to know how to make this specific query faster.

 

A note of caution though - adding too many indexes, even if they work well for a query - can be detrimental to the table's overall performance. Always feel free to consult with your support engineers to determine if adding the new index will be beneficial.

 

 

Example of a slow query log

First you need to analyze the Slow Queries log under System Diagnostics and to do that, you will need to be able to interpret all the information given in every log entry. I have used my instance as an example on how and in which order to read the slow query information:

slow log.jpg

 

First, I sort my list descending on Total execution time - this is where I can make the biggest difference, because these slow queries are executed most often. I usually don't worry about queries that are in the single or double digits, but everything higher than that for average execution time, I will analyze more closely. Once I have finished my analysis on those findings, I move on to the queries with the highest average execution time.

 

Let us look at a single query that needs improvement and analyze it:

Slow Queries Example:

SELECTFROM (sys_dictionary sys_dictionary0  INNER JOIN sys_metadata sys_metadata0 ON sys_dictionary0.`sys_id` = sys_metadata0.`sys_id` )  WHERE sys_dictionary0.`choice_table` IS NOT NULL

 

Total execution time: 50 Minutes

Execution count: 40.091

Average execution time (ms): 75,72

 

Example stack trace:

glide.scheduler.worker.0

  com.glide.db.query_stats.QueryPattern.setStackTrace(QueryPattern.java:59)

  com.glide.db.query_stats.QueryStats.handleMetaData(QueryStats.java:111)

  com.glide.db.query_stats.QueryStats.addToNursery(QueryStats.java:86)

  com.glide.db.query_stats.QueryStats.recordQuery(QueryStats.java:74)

  com.glide.db.query_stats.QueryStats.recordQuery(QueryStats.java:63)

  com.glide.db.DBI.saveStats(DBI.java:1035)

  com.glide.db.DBI.sqlVerbosity(DBI.java:1029)

  com.glide.db.DBI.executeStatement(DBI.java:785)

  com.glide.db.DBI.executeStatement(DBI.java:766)

  com.glide.db.DBQuery.executeAsResultSet0(DBQuery.java:290)

  com.glide.db.DBQuery.executeAsResultSet0(DBQuery.java:265)

  com.glide.db.DBQuery.executeAndReturnTable(DBQuery.java:231)

  com.glide.db.DBAction.executeNormal(DBAction.java:197)

  com.glide.db.DBAction.executeAndReturnException(DBAction.java:166)

  com.glide.db.RDBMSQueryContext.executeQuery(RDBMSQueryContext.java:46)

  com.glide.db.DBQuery.execute(DBQuery.java:1772)

  com.glide.choice.ChoiceDependency.<init>(ChoiceDependency.java:39)

  com.glide.choice.ChoiceDependency.get(ChoiceDependency.java:28)

  com.glide.choice.ChoiceChangeListener.expandMe(ChoiceChangeListener.java:49)

  com.glide.choice.ChoiceChangeListener.flushChanges(ChoiceChangeListener.java:37)

  com.glide.db.AChangeListener.onExecute(AChangeListener.java:43)

  com.glide.db.DBAction.processListeners(DBAction.java:143)

  com.glide.db.DBAction.executeAndReturnException(DBAction.java:171)

  com.glide.script.GlideRecordITable.delete(GlideRecordITable.java:177)

  com.glide.script.GlideRecord.delete(GlideRecord.java:4913)

  com.glide.script.system.GlideSystemUtilDB.tableClearByTable(GlideSystemUtilDB.java:523)

  com.glide.script.system.GlideSystemUtilDB.cleanupTableReferences(GlideSystemUtilDB.java:504)

  com.glide.script.system.GlideSystemUtilDB.dropTable(GlideSystemUtilDB.java:741)

  com.glide.script.system.GlideSystemUtilDB.js_dropTable(GlideSystemUtilDB.java:428)

  ...

  org.mozilla.javascript.gen.c5175.call(sys_script_include.77e30b080a00052638e7274157b9b97a:30)

  org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1227)

  org.mozilla.javascript.gen.c3087.call(sys_script_include.bb1996e80a0a0b0f00499e4a748ddfb7:58)

  org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1227)

  org.mozilla.javascript.gen.c3088.call(sys_script_include.bb1996e80a0a0b0f00499e4a748ddfb7:67)

  org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1227)

  org.mozilla.javascript.gen.c5173.call(sysevent_script_action.2227b84a0a0a0b950004868f9ece6509:3)

  org.mozilla.javascript.gen.c5173.exec(sysevent_script_action.2227b84a0a0a0b950004868f9ece6509)

  com.glide.script.ScriptEvaluator.execute(ScriptEvaluator.java:233)

...

  com.glide.script.fencing.GlideScopedEvaluator.evaluateScript(GlideScopedEvaluator.java:192)

  com.glide.policy.ScriptActionHandler.executeScriptInScope(ScriptActionHandler.java:144)

  com.glide.policy.ScriptActionHandler.process0(ScriptActionHandler.java:62)

  com.glide.policy.ScriptActionHandler.process(ScriptActionHandler.java:39)

  com.glide.policy.EventProcessor.processEventDuringNormalOperation(EventProcessor.java:164)

...

  com.glide.policy.EventManager._process(EventManager.java:160)

  com.glide.policy.EventManager.process(EventManager.java:142)

...

  org.mozilla.javascript.FunctionObject.doInvoke(FunctionObject.java:597)

  org.mozilla.javascript.FunctionObject.call(FunctionObject.java:504)

  org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1227)

...

  com.glide.schedule.JobExecutor.execute(JobExecutor.java:79)

  com.glide.schedule.GlideScheduleWorker.executeJob(GlideScheduleWorker.java:177)

  com.glide.schedule.GlideScheduleWorker.process(GlideScheduleWorker.java:124)

  com.glide.schedule.GlideScheduleWorker.run(GlideScheduleWorker.java:56)

 

Example URL: events process

First sighting: 2015-09-22 17:10:33

 

The Explain Plan

OrderSelect TypeTableTypePossible KeysKeyKey lengthRefRowsExtraID0SIMPLEsys_dictionary0ALLPRIMARY25.629Using where11SIMPLEsys_metadata0eq_refPRIMARYPRIMARY96empigeist2_2.sys_dictionary0.sys_id11

 

The Database Indexes

TableReference TableIndex
sys_scopesys_scopesys_metadata
referencereferencesys_dictionary
element, nameelementsys_dictionary
sys_idPRIMARYsys_metadata
sys_idPRIMARYsys_dictionary
sys_packagesys_packagesys_metadata
name, elementnamesys_dictionary
sys_update_name, sys_class_namesys_update_namesys_metadata

 

 

This is all the information available to you in the Slow Queries log. I will explain them in the order in which they make most sense to me - from the time the query was issued to the additional information that helps us understand what is going on.

 

Interpreting the slow query log information

Query Source

Through the example URL we know that this query was issued by the events process. Another common example URL might be /home.do - meaning the slow query is on a user's home page. The stack trace confirms that this query comes from a worker:  com.glide.schedule.GlideScheduleWorker.run(GlideScheduleWorker.java:56).

 

Following the stack further, I see org.mozilla.javascript.gen.c5175.call(sys_script_include.77e30b080a00052638e7274157b9b97a:30), which refers to a script include called TableDrop, which in line 30 gs.dropTable(tableName); which includes - of course - querying the dictionary record for which table to drop.

 

Query run times

Now that we know the background of this query - how often did it run, how long did it take? The answers to this can be found here:

Total execution time: 50 Minutes

Execution count: 40.091

Average execution time (ms): 75,72

 

Then the question becomes - why does it take so long for a simple query?

 

Query analysis

The query is:

SELECTFROM (sys_dictionary sys_dictionary0  INNER JOIN sys_metadata sys_metadata0 ON sys_dictionary0.`sys_id` = sys_metadata0.`sys_id` )  WHERE sys_dictionary0.`choice_table` IS NOT NULL

 

What does this query do?

It first queries the sys_dictionary table for all records where choice_table is not null. Then it joins the result of that with the information in the sys_metadata table for all records where the sys_ids match.

 

Interpreting an explain plan

Why does this take so long? To find that out, we look at the explain plan:

OrderSelect TypeTableTypePossible KeysKeyKey lenRefRowsExtraID
0SIMPLEsys_dictionary0ALLPRIMARY25.629Using where1
1SIMPLEsys_metadata0eq_refPRIMARYPRIMARY96empigeist2_2.sys_dictionary0.sys_id1Extra1

 

 

This is the real interesting part - what does all this mean?

The more common Select Types are:

  • SIMPLE; no UNION or subquery
  • PRIMARY; outermost SELECT
  • UNION; second or later SELECT in a UNION
  • SUBQUERY; first SELECT in subquery
  • DERIVED; derived table (subquery in WHERE clause)

 

The most commonly seen types are - in order of efficiency:

NameDescription
constAt most, one matching value, treated as a constant.  I.e. active = 1
eq_refJoining or looking up unique index values.  JOIN uses a unique index or key prefix.  I.e. joining two tables on the PRIMARY key.
refJoining or looking up non-unique index values.  JOIN uses a non-unique index or key prefix.  Indexed fields compared with ‘=‘, ‘!=‘. Best data access strategy for non-unique values.
index_mergePerform several index searches using different keys from same table  and merge the results.
rangeRange index scan. The key column is compared to a constant using operators like BETWEEN, IN, >, >=. I.e. sys_created_on > “2015-09-23 00:00:00”.
indexFull Index scan. The entire index tree is scanned.
ALLFull Table scan.  The Entire table is scanned.

 

Now that we have all this information, what does our explain mean:

OrderSelect TypeTableTypePossible KeysKeyKey lenRefRowsExtraID
0SIMPLEsys_dictionary0ALLPRIMARY25.629Using where1
1SIMPLEsys_metadata0eq_refPRIMARYPRIMARY96empigeist2_2.sys_dictionary0.sys_id11

 

We are querying two tables, sys_dictionary first, and then sys_metadata. In the case of sys_dictionary, we use a full table scan through the primary key (sys_id) and have to scan through 25.629 records, that we then filter out from there through the where clause. On the sys_metadata table, we merge the results of the first query record by record based on the primary key, sys_id.

 

Why did it use the primary key and a full table scan on the dictionary query? The answer is simple - there is no index on choice_table, so it had no matching index to go with. If we were to add an index (non-unique, since we do not create unique indexes for query tuning) on choice_table, our explain would then look like something like this:

 

OrderSelect TypeTableTypePossible KeysKeyKey lenRefRowsExtraID
0SIMPLEsys_dictionary0refPRIMARY, choice_tablechoice_table405.427Using index; using where1
1SIMPLEsys_metadata0eq_refPRIMARYPRIMARY96empigeist2_2.sys_dictionary0.sys_id11

 

As you can tell, the extra now tells us an index was found and used. The number of rows we had to comb through to get to our records is significantly smaller, and it returns way faster than before.

 

Talking about the extras, here is a sampling of what they can be:

  • Using filesort; filtered rows are gone through again for sorting
  • Using index; all data is retrieved from a single index (covering index)
  • Using index; using where; index is also used for all key lookups
  • Using temporary; a temporary table is required to process the query
  • Using where; a WHERE clause is used
  • Using join buffer; rows are re-used from the join buffer

 

Finding the best index for your query

How do you know if an index will be good and taken or not - looking at the query is the first step, but there is also this thing called "cardinality." At one point, the database will decide that taking the index has no benefit and will revert to a full table scan. You will look at the explain plan very confused and say "I gave it an index, why would it not take it???"

 

Imagine the following situation: You are searching for all users named Jim that work in the Finance department. Your company has a total of 30000 users, out of those, 500 are named Jim, there are nearly a thousand different names in your list of users, and you have 15 departments in your company (meaning about 2000 users per department).

 

The story of cardinality is - how can I narrow down my results fastest, so I don't have to search through too much information. In our search for Jim in Finance - are we better off searching for Jim first, or for the Finance Department?

 

If we search (and index) for Jim first, we will go through an index tree that has nearly a thousand sorted nodes (all the different first names). If we search (and index) for departments first, we will go through only 15 nodes. But if you look at how many records it would find within the record, you would have 500 for the name, and around 2000 for the department, meaning searching and indexing for name first will narrow our results down fastest and furthest.

 

Different situation - you want to find all closed incidents in your system. 99% of all your incidents are closed. Will an index on state help? Most likely not, since there are so many closed incidents compared to the complete set of data, that it very likely will revert to a full table scan at that point.

 

Sometimes you need to change they query, not the index

And of course there are situations where an index cannot be used at all. We all love the "contains" searches that ServiceNow offers. In SQL speak, they translate to a LIKE '%xxx%', or a double wildcard. Databases cannot use an index on that type of query at all - always ending up with a full table scan (unless there is a saving grace of another part of the query that can be indexed)

 

Another example are OR queries against different fields: WHERE first_name="Jim" OR last_name="Smith" - in this case, it will first search all Jims, then all Smith's and merge the results together - it cannot use one index for both. We always recommend making those two queries and merge the results through other methods.

 

One more common example - if you group by one thing and order by another, the same situation occurs - what to take? Then I won't take any... : GROUP BY first_name, ORDER BY last_name

 

---

 

Special thanks to our fantastic Performance Engineer Scott Nemes at ServiceNow who reviewed and provided some of those great tables that I included in here. Scott, you are the BEST!

So now that I have this nifty way of turning a GlideRecord into an object array, AND I have that in a Script Include function library, what's next?  Well, how about extending the GlideRecord object to include the new toObjectList function?

 

I have seen things like this done in ServiceNow to extend the JavaScript language, but they use a Global Business rule which loads with every table call (no matter what).  This adds to the overall activity load, and does not put forth the extension into every corner of ServiceNow.  I like a different approach, lets write a Script Include that we will call on demand that provides the requested functionality.  No performance load and it goes away when our code has completed!

 

New stuff!

 

GlideRecord.prototype.

GlideRecord.restorelocation()

gs.include(...)

JSUtil.describeObject(...)

 

 

Lab 1.1 - Create a Script Include to Extend GlideRecord

 

With this lab we will be creating a Script Include function library that we will call with a gs.include statement.

 

1. Navigate to System Definition -> Script Includes. This will display the Script Includes list view.

2. Click on the New button.  This will display the new Script Include form.

3. Fill in the form with the following:

    a. Name: GlideRecordExtensions (this will trigger the creation of a Script template; delete this before entering in the script below).

    b. Client Callable: False

    c. Active: True

    d. Script:

 

// These functions will be tacked onto the GlideRecord object when we new it.
// You can add functions and properties to existing objects in this manner
// The "this" variable is actually the GlideRecord object!
GlideRecord.prototype.toObjectList = function() {
    var objectList = [];
  
    // loop through all the records and create the object array
    while(this.next()) {
        objectList.push(this.toObject(this));
    }
    this.restoreLocation(); // set it back to the beginning so that we can use if for other things

    return objectList;
};

// Turn a single GlideRecord record into an object
GlideRecord.prototype.toObject = function(recordToPackage) {
   var packageToSend = {};

   for (var property in recordToPackage) {
       try {
           packageToSend[property] = recordToPackage[property].getDisplayValue();
       }
       catch(err){}
   }

   return packageToSend;
};
  

 

 

Lab 1.2 - Testing the New Extensions

 

1. Navigate to System Definition -> Fix Scripts.  This will display the Fix Scripts list view.

2. Click on the New button.  This will display a new Fix Scripts form.

3. Fill in the form with the following:

    a. Name: GlideRecordExtension Test

    b. Active: true

    c. Script:

 

 

// This brings in our new functionality (so to speak)
gs.include('GlideRecordExtensions');

var incidents = new GlideRecord('incident');
incidents.addActiveQuery();
incidents.setLimit(10);
incidents.query();

gs.print(incidents.getRowCount());

// Now invoke our new GlideRecord extension!
var stuffList = incidents.toObjectList();

// Check to see if our list is now populated
gs.print(stuffList.length);

// It is! Now pick one of the objects out of the array and see if it's properties are populated
gs.print(stuffList[5].number);

// They are!  Now describe that entire object to see if it was a fluke
gs.print(JSUtil.describeObject(stuffList[5]));

// Everything is filled in like it should be!
  

 

4. Click the Submit button to save your work.  The Fix Scripts list view will be displayed.

5. Click on the name of your new Fix Script.  This will display your Fix Script for editing.

6. At the bottom of the form click on the Run Fix Script link in the Related Lists section.  This will display the run Run Fix Script form.

7. Click the Ok button to continue. This will display the warning form.

8. Click the Proceed button to continue.  You should have a results form that displays something like the following:

 

1. Fix Script run.JPG

 

 

Now that wasn't so bad.  In the next article I will use this functionality along with a new ECMA 5 array method called .filter to show off yet another nifty bit of capability.

 

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!

CS_logo.jpgMVP-logo.jpeg

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

 


for Click for More Expert Blogs and also Find Expert Events!


Let's talk about my favorite part of the Community: questions. I love reading the myriad questions posted here, chiming in when I think I know the answer, and seeing the care and attention so many people are putting into helping others.

 

Sometimes though... sometimes I see a question and my initial response is:

 

Huh?

 

huh.jpg

 

Some questions are framed poorly; others lack the context required to solve the problem. And some questions are asked in such a way that all the words individually make sense, but when put in that specific order end up being meaningless. And that sucks, because every question is a request for help, and helping each other is one of the core foundations of any community. If you structure your questions in the best possible way, you'll get the best possible answer.

 

But why listen to me?

 

I know a thing or two about a thing or two.

 

Before I was a developer at ServiceNow, I was on the Support team. Before that, I held several technical support and web development roles at other companies. Altogether I've been fixing problems and helping others fix problems for about 15 years. I know what I need to see in a problem description to quickly understand the context of the question and the likely places to look for a solution- which sometimes means addressing it in a different way than the question was framed. This month is my 4-year anniversary at ServiceNow, and in that time I've answered hundreds of Incidents, pushed thousands of lines of code, and generally tried my best to make sure that every question that comes across my desk has been answered fully.

 

 

What makes a question easier to answer?

 

When I can look at new questions on the Community, I look for questions that have purpose, context, and clarity. If your question displays those three qualities, I know that you've already done the basic research and it's unlikely the answer can be found with a simple Wiki search. It's interesting and therefore likely to draw attention. It also has everything I need to get started on a solution.

 

What is Purpose?

 

Purpose is the problem you are trying to solve. It's the use-case or business case for the question.  Purpose tells people what the end result is going to be, not necessarily the specific difficulty you are having. You should be able to define why you are doing the project- why are you updating this Business Rule, why you're writing this Script Include, or why it's important that this field on your form have a Red highlight for Itil users but a Green highlight for their managers. In some cases knowing the purpose can help us determine if the solution you are struggling with is the best way to accomplish your goal. perhaps you've spent the better part of a day wrestling with the login in an after Business Rule when the best way to solve your issue is with an event. For some examples, look at just about any question posed by rfedoruk :

 

Content created by rfedoruk

Screen Shot 2015-10-05 at 2.13.57 PM.PNG

 

Every question includes purpose. There is a problem to be solved, an attempted approach and finally a specific issue with this approach that is yet to be overcome.

 

 

What is context?

 

When you describe the issue you are having, it's almost always necessary to know where this is occurring. Let's say you're having an issue with a specific script. You must, at the very least, post the problem you have and the content of the script in question:

 

The answer from my Script Include is always null when my script runs.

 

var ga = new GlideAjax("x_emp.DataGenericUtil");
ga.addParam("sysparm_name","doStuff");
ga.addParam("sysparm_input", g_form.getValue("u_foo"));

ga.getXML(myCallback);



 

With this example, we may be able to determine that the "foo" field on the table is probably not supposed to be prefixed with "u_", since this is likely running on a custom table in the app scope "x_emp". Likewise, we could probably say that the "x_emp" prefix on the script include name is not needed here. However, those would be guesses- and pretty poor ones if this is actually a Client Script running on the global table "Foobars" which really does have a field named "u_foo" on it, and it's calling a scoped Script Include called "DataGenericUtil" in the scope "x_emp", even though the script itself is in the global scope.

 

To get help with this script, we'd need to also include the content of the DataGenericUtil Script Include's doStuff function:

 

    doStuff : function() {
        var input = this.getParameter("sysparm_input");
        gs.info("Parameter sent was {0}", input);

        return "plonk";
    }



 

We also need to know if the script was marked Client Callable, if it's accessible from All application scopes or This application scope only, and if it's currently marked Active.

 

We need to know about the table this runs on:

  • Does it have a u_foo field?
  • What kind of data does it have?
    • Is it a String field or a Date field?
    • Does it have instance-specific formatting?
  • What scope is the table in?
  • Does the field have data at the time the script runs?

Speaking of our script, we need more information about that as well:

  • What kind of script is it?
    • Client Script?
    • UI Script?
    • Does it run onLoad, onChange, or onSubmit?
      • If it's onChange, what field does it react to?
  • What scope is the script in?
  • What is the content of the myCallback function?

 

That's not to say that we want to see everything. Include enough code so that someone can reproduce the problem- or set up a minimal test-case of your own on one of our demo instances, and point people to it. We want to be able to examine the relevant code and run the program- in our heads if it's small enough, or on a live system exhibiting the problem if it's really complicated.

 

Finally we want to know if any errors are thrown. This script runs client-side, but it also invokes a server-side script to do some processing. Errors on either side can cause unexpected results. We want to know if you are seeing errors in your browsers console, if there are any errors in your System Logs, and if there is anything that stands out around the transaction in your node log. Errors thrown by your code are designed to be descriptive. A good error message tells you what went wrong, where it went wrong, and (usually by context) how to fix it.

 

That seems like a lot of work for one measly script question, but that's only because you don't need to be brought up to speed on the whole problem- you're already in the middle of it. For the rest of us, the more relevant information you provide, the less time we spend trying to catch up from the back, the quicker we can join you in the middle, and the quicker we can push forward to the solution.

 

 

What is clarity?

 

Some problems in technology are truly hard. One of them is being able to describe a problem with a system that you are intimately familiar with, to a person who isn't familiar with it. There are at least 2 potential language barriers: are you both able to express yourselves in the same language, and do you both understand the same jargon? And there are differing levels of ability- some people have been troubleshooting issues for years, and other people just started learning the platform.

 

When you describe your problem, you want to make sure that the person reading it comes away with a good idea of exactly where this issue occurs. Write in complete sentences. Separate multiple questions into a list, or into multiple paragraphs. Add screenshots to the text near where you reference them. Don't abbreviate unnecessarily. If you don't know the jargon for something, that's OK; if you can describe it well enough, name it, and then refer to it that way consistently then can still communicate about it.

 

Writing in complete sentences, capitalization and punctuation are important. Humanity has been communicating ideas via the written word for millennia. These conventions exist because they are useful. They improve the presentation of your ideas, and they make consumption of them faster. You don't have to be an English major with a Ph.D. in the classics to get your point across- but if you are, think Around the World in 80 Days not Finnegans Wake.

 

Your question title is the first thing people when see when they look at the posts on the Community. Do you think it accurately describes the question you have? Does it sound like an interesting problem to solve? Here are some examples of good and bad problem titles:

 

Bad: script value null

Good: Answer from Script Include is null, only when called from scoped Client Script

 

Bad: Javascript error

Good: What is causing "ScopedGlideAjaxGenerator is undefined" error when loading Incident on tablet interface?

 

Bad: Business Rule doesn't work

Good: gs.eventQueue does not insert events when called from Before business rule on Task table

 

 

Bringing it all together

 

So with the script problem I invented earlier, a post like the one below is the most likely to get accurate help quickly. As a bonus, just by writing the problem out and thinking through it logically, you might even come to a resolution before you even submit!

 

 

Answer attribute is null in GlideAjax response to scoped Script Include

 

I am creating a scoped custom application on my instance, which adds a Client Script to a table outside of my scope. This table is part of a legacy system I have inherited, and I haven't been able to recreate it and merge it into my scoped application yet. The client script in question is an onChange script, that fires on changes to the u_bar field, and sends the value of u_foo to the server. I expect to get the result "plonk" back in my answer attribute on the GlideAjax response, but it is just a null value. The u_foo field contains the number of people who have agreed to attend an event (it's an integer field) and u_bar is the location of the pub we are going to (a reference field to cmn_location). Every time we change pubs, the number of people in u_foo changes, and we want to track that in real time so we know the pub most likely to boost attendance.

 

This is my onChange script:

onChange(newValue,oldValue,isLoading) {
    if (isLoading)
        return;

    console.log("New bar value: " + newValue);
    console.log("Foo value: " + g_form.getValue("u_foo"));

    var ga = new GlideAjax("DataGenericUtil");
    ga.addParam("sysparm_name","doStuff");
    ga.addParam("sysparm_input", g_form.getValue("u_foo"));
    ga.getXML(myCallback);

    function myCallback(response) {
        var answer = response.responseXML.documentElement.getAttribute("answer");
        console.log("Response from server is: " + answer);
    }
}




 

My Script Include is pretty basic. I took an existing include which was doing the work server-side and made it client-callable:

var DataGenericUtil = Class.create();
DataGenericUtil.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
    initialize : function() {
        //nothing to do here
    },

    doStuff : function() {
        var input = this.getParameter("sysparm_input");
        gs.info("Parameter sent was {0}", input);
    
        return "plonk";
    },

    type: 'DataGenericUtil'
});



 

When I converted it, I made sure to mark it Client callable. It was already Active, obviously, but I did have to extend AbstractAjaxProcessor. I already tried extending global.AbstractAjaxProcessor (as you can see) but that hasn't fixed it.

 

I have verified that the fields on this table exist- the console.log calls output a valid sys_id and an integer between 1 and 25 each time it runs. I don't get any errors in my JavaScript console, and the instance logs are similarly barren.

  1. Is there additional logging I could do to help track this down?
  2. Does the table this script runs on need to be in the same scope as the client script?
  3. Would it help to capture the request headers or the response directly, and post them here?

 

 

 

 

I hope this post is useful. I really do love coming to the Community and trying to solve problems. If you have a question to ask, spend a little extra time doing the write-up, and I promise that you'll get it all back plus more when the answers start rolling in.

I have strange and mystical powers.

Calling all CreateNow Developers! The Native Platform Features course is now live on the Developer site! The training module consists of 14 exercises on the platform's native features to train you to customize it for your business. 

 

This course contains tutorials on:

  • Workflow
  • Service Catalog
  • Scheduled Jobs
  • Reporting


These features are available out of box and are completely customizable.

 

Workflow

In the Workflow course, we expand upon the concepts you learned in the Building a Sample ServiceNow application.  During that course you created a workflow that had an automatic approval of anything less than $500 dollars.  In the Native Platform Features you will create a new activity that automatically assigns approvals to an approval group instead of an individual user.

 

Service Catalog

Curious about ways to populate the Service Catalog? The Service Catalog module will take you through using an existing Record Producer to populate the Service Catalog.  It will also teach you how to create a new Service Catalog and add it to the homepage.

 

 

Catalogs.jpg


Scheduled Jobs

Automation is key, are you responsible for collecting and disseminating data to your team?  If you are, the Scheduled Jobs course will teach you how to automate the generation and sending of a weekly report. 

 

Reporting

One of the most important aspects of your instance is your data! Reporting teaches you how to create reports using your own data. In the Reporting course you will learn how to take the data from your own application and generate reports with it.

 

Check out the Native Platform Features course to learn more!

We've spent the past few months asking questions, collecting data, and analyzing your ServiceNow mobile UI needs. This intense month-long project has resulted in the well-stocked Mobile resource page you see below. The ServiceNow resource pages highlight Knowledge Base articles, product documentation, community solutions, and video demos available for said topic. We've done our best to make sure no stone is left unturned with regard to issues you may be having in the mobile interface.


Take a look! See if these resources answer your questions, and share it with your fellow cube-mates! As always, send us your feedback here or directly in the article. Happy reading!


All new: Mobile User Interface Resources page


servicenow mobile UI.jpg


What do you think? Did our resource page help you find the latest info on your question or issue? Let us know by giving it 5 stars!


You can also check out our other resources pages:

All new HI Notifications Resource page

All new User Interface (UI) Resource page

All new Authentication resource page

All new Discovery and MID Server Solutions resource page

All new Update Set resources page

All new Reporting resources page

All new CMDB resources page

All new Upgrading resource page

This list represents the Mobile UI Known Errors with the highest number of incidents associated with them.  If you are affected by any of these incidents.  Please take advantage of the new subscribe feature to stay informed of any updates or fixes for that KE. The majority of these known issues are found in the Eureka and Fuji Releases, and a few in Dublin.

 

Please note, some articles may require you to log in to HI. See this link for information on how to  subscribe to Known Error articles.

 

 

TitleNumberDescription
Mobile App does not work with SAML on EurekaKB0551083[Mobile App] When a customer is upgraded to Eureka and tries to use the IOS mobile app with SAML, the application hangs with no indication of why it is failing. The mobile app is hanging because it did not trust the customer's certificate.
Smartphone Mobile UI - Word wrap does not work for description fieldsKB0551128In the mobile UI, word wrap does not work for description fields.
g_form.getReference does not work on Mobile UIKB0551077

When using g_form.getReference, nothing happens, as there is an error in the background:

"Could not load onChange client script 'script_name': SyntaxError: Unexpected token"

Issues with advanced reference qualifiers in the mobile user interfaceKB0541017Cannot set field values on redirect to a form for the mobile UI. Also cannot use script include calls in advanced reference qualifiers for the mobile UI.
Default value for Select Box variable is ignored in mobile service catalogKB0551088When accessing Service Catalogs within the Mobile UI, the default value for the select box variables is ignored.
The action.setRedirectURL does not work in mobile UI actionsKB0533278[Dublin] action.setRedirectURL doesn't work in Mobile UI Actions
Mobile interface showing duplicate icons for [sys_ui_home_favorite], [sys_ui_home_section] and [label] records for some usersKB0546754Mobile interface showing the duplicate icons for [sys_ui_home_favorite], [sys_ui_home_section] and [label] records for some users.
Entering text in to the password field on the mobile login page causes the screen to blank on iPhoneKB0551129Unchecking the "Remember Me" option on the mobile login screen and then entering text into the password field causes the screen to go blank on the iPhone.
Cannot scroll Incident form on iPad in portrait modeKB0551082[Tablet] The user cannot scroll horizontally on the incident form on iOS. The form does not resize correctly on the iPad.

 

The majority of these issues have been fixed in available releases, which are specified in the associated Knowledge Base articles.  If you are experiencing any of the above issues that are not yet fixed, you can now subscribe to Known Error articles to be notified of any changes and updates made to the "fixed in," "seen in," "workaround" and "steps to reproduce" sections.

 

If you're experiencing a product issue not covered in Known Error or Knowledge Base articles, please post the bug to the community.  Our members and internal employees will do our best to offer guidance and document as necessary.

As we reported in our previous blogs -  part 1 and part 2, one of our customers held a very successful hackathon that concluded today.  After the event, I spent some time with Shiva Ramachandran(SR) their IT Director and the main sponsor for the event this year to get his thoughts on the event.

 

NS > Shiva, congrats on pulling off a very successful hackathon. I liked the super hero theme and how it blended creativity with fun. How do you feel?

SR> Very excited to see the energy and enthusiasm of all the teams and the creativity that has been exhibited.  shiva.jpg

 

NS > This is the second year your company is running a hackathon on ServiceNow – what was the original inspiration for this event and what value do you see for your organization?

SR>  We have folks coming up with ideas all the time.  We wanted to channel that creativity and provide a conduit by allowing a few days for them to work on some of the areas that they are very passionate about.  As you can see all these ideas are areas where the teams are trying to solve the problems faced on a day-to-day basis.

 

NS >  I was really impressed by the business value of these apps - these were not just apps that were cool for its own sake, the teams could articulate the cost saving or productivity improvements in quantifiable terms.  What are your plans to take the promising apps that were developed in this event and actually roll it out in your environment?

SR>  Many of the ideas that were created at the hackathon last year have already been implemented and are in production.  We plan to do the same this year.

 

NS > Any best practices that you would want to share with others in the ServiceNow community that may want to hold their own hackathon?

SR>  Planning and communication are the key.  You need to start with leadership to champion this and have your customers and other stakeholders participate in it.  Communicate, communicate, communicate.  Make the team excited so that they can work on this.  Clear their plate so that they can focus on this activity.  Work with ServiceNow teams to have their support so that they can provide the instances and help with folks during the hackathon.  Most importantly have some fun!

 

NS > That is great advice.  Final question, what is it with the weather - it is always this hot in Phoenix?  It is supposed to be fall and it is 102 degrees.

SR> Ha ha ha…..  Not always.  It starts to cool down in October and becomes good weather till April.

 

NS > Thank you Shiva for letting us be a part of your event and even letting me wear the superhero cloak.  I look forward to seeing you at Knowledge 16 and attending your next hackathon!

Poster-smaller.jpg

The coding is done, the red bull supply has run out and now it is time for judges to pick the winning entry.  As @Martin reported in his blog yesterday one of our large customers held a hackathon on ServiceNow platform - their second year holding such an event.  Today each team was given 15 minutes to do a demo of their app to a panel of judges. The team names Mighty Legends, Blue Monsters, Flying Robots etc. were in tune with the superhero theme of the event and added to the fun.  It was truly amazing to see what the teams had built in about a day - a mobile app with speech recognition, dev op tools, integrating change management with Lync and many more.  Each of these apps either solved an existing issue in their current environment or showed how a simple app can help save money or help them work more efficiently.  As an example, one of the teams figured out that better collaboration would help them meet or exceed their SLA commitments.  Another team built an app to help quickly size a project effort and an estimated cost savings of $500K.  Not bad for about a day's work!

 

 

 

 

 

The grand prize was awarded to a team that built an elegant solution to enable code reuse and sharing - they built a component repository that enables anyone in the organization to easily share code with each other.

21842272606_ff816fbdbc_z.jpg

This is similar to ServiceNow share in intent but is designed to handle code sharing in any language and is meant for internal use.  Developers can contribute to the repository by filling out a simple form and others can search, download and use it.  The front end was built using AngularJS to give it a slick, modern interface.  Reuse is a hallmark of good software engineering and thanks to the ingenuity of this team and the power of ServiceNow platform developers can build apps faster than before. Our hearty congrats to the winners and all the teams!             

 

Part 3 : Interview with the sponsor of the event>>

I spent some time this morning looking for a link or a document about the unique environment Client Scripts which are part of a Scoped Application execute in. I wasn't able to find an official document outlining the details, not in the Wiki, nor in the Developer API Reference, nor in the KB on Hi. Since I implemented most of this functionality, I thought I'd write up a blog post explaining it. We'll also work on official documentation in the correct places, of course.

 

What are the differences between a scoped Client Script and a global one?

 

Scoped Client Scripts- Client Scripts which are created as part of a Scoped Application- are executed slightly differently than their non-scoped counterparts. They are wrapped in a closure, and certain APIs that would normally be accessible are either modified, or unavailable altogether. We'll get into the reasons later on, but here is a quick rundown of the differences.

 

Inaccessible APIs

Modified APIs

  • GlideRecord
  • window
  • document
  • $ (Prototype library)
  • $$ (Prototype library selector shortcut)
  • jQuery
  • $j (jQuery shortcut)
  • $F (Sizzle form element value shortcut)
  • GlideAjax
  • g_form
  • GlideDialogWindow

 

 

Most of the inaccessible APIs can be re-enabled on a per-application basis. To do so, you need to ship a True/False system property in your application named glide.script.block.client.globals with the value false. As with all system properties in a Scoped Application, the name of the property will automatically have your application's scope name prefixed to it- you don't need to do that yourself. I see access to these global objects as a failure on our part to provide appropriate APIs. Direct DOM manipulation is sometimes the only way to achieve a certain goal, but it's also prone to breaking between releases. Something that worked fine in UI11 may not work in UI15, for example. By tracking which scoped applications needs these globals, we can better target our development efforts on creating or updating APIs to address those needs without resorting to the DOM.

 

I said most of the inaccessible APIs can be turned back on. There is only one that is not available even with that system property set to false- client-side GlideRecord. This should be considered deprecated. I would like to have removed it completely, but there is a lot of legacy code out there in the wild using it. Client-side GlideRecord has some convenience, but it comes at the cost of large payloads, and the fact that almost everybody using it does so synchronously. Everything client-side GlideRecord does can be done with GlideAjax and an appropriately-written Script Include or processor.

 

So what about the modified APIs?

 

The version of GlideAjax that is available to scoped Client Scripts can only make asynchronous requests. Requests made from scoped GlideAjax requests will also take place within the application's scope. You don't need to prefix your scope name on calls to your Script Includes, and other scoped Client Scripts have to respect your access policies.

 

MethodRestrictionAlternative
getXMLWait

Not Supported

getXML(callback)

getXMLAnswer(callback)

 

The scoped version of g_form is slightly more involved. Any calls to getReference must provide a callback function. This makes it an asynchronous action, and doesn't hang the interface while the client is communicating with the server. Scoped applications cannot change the Mandatory, ReadOnly,  Display, or Disabled status of fields outside of their scope- with one interesting exception. If your Client Script runs on a table in your own application, and that table extends a table from another scope, the fields you inherit from that base table are considered in-scope when they are displayed on your form.

 

What?

Consider the difference between adding a field to the Incident table and extending Incident with a new table that has only one field. In the first case, your field is being displayed on Incident. Client Scripts you add to the Incident table can do all the regular things with your field but are limited in what they can do to other fields. You cannot make the Caller mandatory and disable the Category field, because that could negatively affect the functionality of other Client Scripts and UI Policies on the Incident table.

In the second case, where you extend Incident, you are inheriting the fields that are Incident (and in its parent chain), but nothing you do on this table can actually break the functionality of Incident itself. You can set fields to be Mandatory or Disabled or ReadOnly as your requirements dictate, without worrying that you might be breaking some other application. Similarly, fields added onto your table by other scopes (if your Design-time Table Access is set up to allow that) cannot break your application's functionality. If they add Client Scripts to your application, they can't break your carefully-crafted rules about which fields must be filled in by the user before submission, and they can't hold up your user's interaction because they want to make a lot of Ajax calls as part of their functionality.

 

MethodRestrictionAlternative
setReadOnly/setReadonlyCan only apply to fields in the same scope as the calling script.Extend the table instead of adding a field to it
setMandatoryCan only apply to fields in the same scope as the calling script.

Extend the table instead of adding a field to it

setDisabledCan only apply to fields in the same scope as the calling script.Extend the table instead of adding a field to it
setDisplayCan only apply to fields in the same scope as the calling script.Extend the table instead of adding a field to it
getReferenceMust use a callbackg_form.getReference("ref_field",callback);

 

The scoped version of GlideDialogWindow is only marginally interesting. When making calls to GlideDialogWindow, HTML is generated server-side, and then passed down to the client to be displayed. The change to this API is invisible to your application- it merely ensures that the HTML that gets generated is appropriate for your scope. For instance, if you are rendering a form that contains Client Scripts, those Client Scripts are appropriately scoped as well.

 

You mentioned a closure?

 

Technically it's an IIFE- an immediately-invoked function expression. We wrap your scoped Client Scripts in a strict-mode IIFE to provide the appropriate environment, and to prevent leaking variables into the global scope. This prevents two Client Scripts from accidentally overwriting one another's variables and functions. This doesn't remove access to everything in the global lexical scope, it just makes sure that variables and functions you declare don't change underneath you because someone else also likes those names. The downside to this approach is with sharing functions between Client Scripts. In the old model, you could close your onChange or onLoad script, define a function, and it would be available to your other Client Scripts because it was part of the global window object. With the new approach, shared functions need to be defined in UI Scripts. The new model for Scoped UI Scripts creates an object in the global window with the same name as your scope, and allows you to add properties to this object which are then available to all your other UI Scripts and Client Scripts, just by accessing the object.

 

What?

Ok, I realize I'm suddenly talking about UI Scripts, and we seem to have taken a left turn. Trust me, you want to know this.

Scoped UI Scripts have a specific format that we'd prefer applications to adhere to. It's not a requirement because we recognize that UI Scripts serve many purposes, and that "acting as a library of shared functions" is only one of them. But if you adhere to the format that is pre-filled for you when you create your UI Script, you'll be able to add public methods to an object that you control, and can access those methods just by calling scope_name.methodName() in your Client Scripts (assuming the UI Script has been included on the form your script runs on). You can even define new properties on this object from within your Client Scripts, and therefore make them available to your other Client Scripts similar to how you did in the past- without worrying that you are clobbering some other global variable or function, and without worrying someone else is clobbering yours.

 

That should probably be covered in some Best Practices documents, right?

I agree (not surprising- I wrote that question). In addition to the documentation on Client Scripts, we'll work on some examples that show how we envision all of this being put into practice.

 

OK, but why (the short version)?

 

There were 3 things we wanted to address:

  1. Reduce the likelihood that a misbehaving Client Script or a random client-side JavaScript error could break the interface for users.
  2. Keep the interface responsive at all times.
  3. Reduce the likelihood for conflicts between two Scoped Applications running at the same time.

 

Every change that we made was done to address one of those 3 concerns. Asynchronous Ajax calls ensure that the interface remains snappy. IIFE's  make sure that someone's Floops app can't break your Spork form because they happen to also like the variable name "spoon", or they forgot a semicolon in a crucial place and JavaScript execution was halted. We also wanted to make sure that you don't have to constantly be referring to your own scope name. Client Scripts in your scope should not need to tell GlideAjax that they want to access the "alligatorDefenseProtocol" method on your Script Include named "HippopotamusFactoryGenerator" and not some other application's "HippopotamusFactoryGenerator" (which doesn't even include the "alligatorDefenseProtocol" method- how naive). GlideAjax should just know.

 

By pushing developers away from direct DOM manipulation and into supported APIs, we are trying to ensure that the application you build for Fuji keeps working on Geneva. If you build an application in UI14, it should work when the user switches to UI15. And when a new interface comes along, the app shouldn't need to worry about it- if it's using the supported APIs.

 

Most importantly (to me, anyway) is that users aren't negatively affected by applications. Something that they used yesterday still works today- even if an enhancement was added to the Incident form by way of a sweet new application that makes the entire form turn chartreuse when the Incident caller is a VIP, or which automatically searches previous incidents by the same Company for relevant issues so they can get up-to-speed quicker and deliver an answer that is more on-point than ever before. Enhancements and applications should make the experience better, they shouldn't be able to make it worse.

 

That's a lot of information- what's the short, short version?

 

Yes, it is. And that's not everything that I could say on this topic- I could probably talk for a couple of hours about it. But that's annoying because I tend to wander and get stuck in esoteric side-channels and bore the heck out of anyone unlucky enough to be within hearing range. So here is the short, short version:

 

  • ASYNC ALL THE THINGS
  • DON'T MESS WITH STUFF THAT ISN'T YOURS
  • DON'T ACCESS THE DOM DIRECTLY IF YOU CAN HELP IT
  • BE EXPLICIT WITH SHARED VARIABLES BY PUTTING THEM IN A UI SCRIPT
  • glide.script.block.client.globals IS A WORKAROUND (SOMETIMES)


I hope that was informative.

I have strange and mystical powers.

Some of you might be experiencing the product defect where your reference fields that were made read-only through a UI Policy or a Client Script can still be edited using the look-up icon (aka magnifying glass). An example of this is may be you've made the User field on your incident form read only. Even though the field is greyed out, when you click on the magnifying glass, it allows you to edit the field. This can be risky business because all of your read-only fields are mostly read-only for security purposes.

 

There are two work-around paths for this issue:

 

  • Configure the reference field definition in the dictionary to read-only.
  • Add a read-only ACL to the reference field.

 

 

To configure the reference field definition in the dictionary to read-only.

  1. Right click on the field label.
  2. Select Configure Dictionary

    dictionary.jpeg

  3. Check the read only box.

    dictionary_check_box.jpg

  4. Click Update

 

To add a read-only ACL to the reference field

  1. Elevate your privileges by clicking on the lock icon

    security admin.jpeg

  2. In the form access the context menu by right clicking in the header
  3. Select Configure > Security rules

    security rules.jpeg

  4. Create a New ACL

 

 

For more information see:

Reference fields set as readonly in Fuji still have working lookup magnifying glass

To speed up my usage of Fix Scripts; which I detailed how to use in an earlier article, I decided to add a button. Frankly I just plain got tired going to the bottom of the form and clicking the “Run Fix Script” link!  This was especially an issue, because after you click Save or Update it takes you immediately to the top of the form! Arrrrgh!  Okay, okay, so I am a lazy programmer!  Go figure.


 



Lab 1.1 – Creating a Run Fix Script Button

 

So to add this really great functionality do the following:

 

1. Navigate to System Definition > Fix Scripts.  This will bring up the Fix Scripts list view.

 

2. Right click on the list view header to bring up the context menu, and click on Configure > UI Actions.  This will bring up the Fix Scripts UI Actions list view.


2.UI Actions.jpg

 

3. Search for: Name contains “run”.  This will display the “Run Fix Script” UI Action. Click on the action name to open the form.

 

4. Fill out the form with the following:

    1. Name: My Run Fix Script
    2. Form button: check
    3. Form context menu: check
    4. Form link: uncheck

 

NOTE: We won’t need the form link.  The other UI Action is taking care of that.

 

 

5. Right-click on the form header and choose Insert and Stay. Make sure you do this step as you don’t want to write down over the out-of-the-box original!

 

 

6. Now navigate back to System Definition > Fix Scripts and open up one of the scripts listed in the list view.

 

7. You should now see your new Run Fix Scripts button at the top of the form!

 

8. Right-Click on the form header and you should also see the Run Fix Scripts link in the context menu.

 

And that is all there is to it!  Now you don’t have to scroll to the bottom of your form just to run your script.  Simply click the button!  Cool, huh?!

 

Steven Bell

 


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

Filter Blog

By date: By tag: