Saturday, March 15, 2014

ServiceNow – Why is GlideRecord insert() returning a null



You’re trying to save your spiffy new record but var incSid = current.insert() is returning null.  Boo. 

To make matters more fun, this code is an Inbound Email Action.  One might suspect that the guy responsible for anything with the letters e-m-a-i-l in ServiceNow must really like creating puzzles or hate wasting disk space on silly things known as error messages.

The first thought is that it could be an ACL issue.  You don't have create rights to the table so that must be why it is failing.  The only problem with that theory is that you are an admin and  so theoretically the code is running in your authorization context.

So what now?  Bad data maybe?  Ok, so off to our friend 'Background Scripts'.  Let's try a field that doesn't exist
var x = new GlideRecord('incident');
x.u_something_that_cant_exist = true;

gs.log(x.insert());
Well, that returned a sys_id and not null. But it did seem to return some log messages.

So, this was a fruitful path to investigate. I went ahead and replaced incident with the table in question and voila, we get an error message when running the background script:
Background message, type:error, message: Data Policy Exception:  user is mandatory 
*** Script: null
and there we go. The problem was that it wasn't a bad field but missing data in a mandatory field.

Monday, February 10, 2014

ServiceNow - find the bug

You have a spiffy email notification. Instead of it firing, you get
For input string: "${description}
Priority"java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
java.lang.Integer.parseInt(Integer.java:449)
java.lang.Integer.parseInt(Integer.java:499)
com.glide.notification.substitution.FieldSubstitute.replace(FieldSubstitute.java:56)
com.glide.notification.substitution.SubstitutionManager.substituteToken(SubstitutionManager.java:196)
com.glide.notification.substitution.SubstitutionManager.substituteVar(SubstitutionManager.java:101)
com.glide.notification.substitution.SubstitutionManager.substitute(SubstitutionManager.java:67)
com.glide.notification.substitution.SubstitutionManager.substitute(SubstitutionManager.java:53)
com.glide.notification.outbound.EmailFormatter.substitute(EmailFormatter.java:174)
com.glide.notification.outbound.EmailFormatter.substitute(EmailFormatter.java:167)
com.glide.notification.cmn.NotificationMessage.getBody(NotificationMessage.java:109)
com.glide.notification.cmn.NotificationMessage.processTemplate(NotificationMessage.java:63)
com.glide.notification.cmn.NotificationActionHandler.send(NotificationActionHandler.java:66)
com.glide.notification.cmn.NotificationActionHandler.process(NotificationActionHandler.java:51)
com.glide.policy.EventProcessor.processEvent(EventProcessor.java:73)
com.glide.policy.EventProcessor.process(EventProcessor.java:56)
com.glide.policy.EventManager.processEvents(EventManager.java:187)
com.glide.policy.EventManager.process(EventManager.java:129)
com.glide.script.GlideSystem.js_eventsProcess(GlideSystem.java:618)
inv41.invoke()
org.mozilla.javascript.FunctionObject.doInvoke(FunctionObject.java:565)
org.mozilla.javascript.FunctionObject.call(FunctionObject.java:480)
org.mozilla.javascript.ScriptRuntime.call(ScriptRuntime.java:1196)
org.mozilla.javascript.gen.c440.call(:1)
org.mozilla.javascript.gen.c440.exec()
com.glide.script.ScriptEvaluator.execute(ScriptEvaluator.java:163)
com.glide.script.ScriptEvaluator.evaluateString(ScriptEvaluator.java:64)
com.glide.script.ScriptEvaluator.evaluateStringWithPrefix(ScriptEvaluator.java:40)
com.glide.script.Evaluator.evaluatePossiblePrefixedString(Evaluator.java:231)
com.glide.job.RunScriptJob.evaluateScript(RunScriptJob.java:134)
com.glide.job.RunScriptJob.execute(RunScriptJob.java:79)
com.glide.schedule.JobExecutor.execute(JobExecutor.java:72)
com.glide.schedule.GlideScheduleWorker.executeJob(GlideScheduleWorker.java:163)
com.glide.schedule.GlideScheduleWorker.process(GlideScheduleWorker.java:128)
com.glide.schedule.GlideScheduleWorker.run(GlideScheduleWorker.java:58)
  So let's go look at the 'What it will contain'  and we have
Your incident ${number} has updated information:
Short description: ${short_description}
Updated Information: ${comments)
Original Description: ${description}
Priority: ${priority}

Find the bug?

s/${comments)/${comments}/

Yeah, I know. Syntax checkers are for the weak and feeble.

Sunday, December 08, 2013

ServiceNow - when the "true|false" type isn't

Let's say you have a system property defined in ServiceNow with a type of "true | false".

To access the property, you find the gs.getProperty() call at http://wiki.servicenow.com/index.php?title=GlideSystem#getProperty.28String.2C_Object.29.  You then have something like:
var isValue = gs.getProperty("some.true-false.property");

if (isValue) {
  gs.log("is true");
} else {
  gs.log("is false");
}
What's the bug in the code?
gs.log(typeof isValue)
returns "string".

gs.getProperty() will always return a string value.  Instead of it being documented at the link above, you can see this in the table dictionary definition and it is documented at http://wiki.servicenow.com/index.php?title=Adding_a_Property.

Maybe I'm just old but I really don't like dynamically typed languages, especially when you have almost no debugging environment to speak of.

Saturday, October 26, 2013

ServiceNow LDAP, Groups, Roles

A few things to know when writing Javascript in Service Now:

1) When importing groups and users from LDAP, you need to make sure that you map the dn from LDAP to the source field in the sys_user table, as part of the transform map.  Otherwise the OnAfter call of ldapUtils.addMembers(source,target) during the group transform will not be able to associate the user with a group when you go to sync the group.

2) When manipulating roles, you need to modify the sys_user_has_role for users and sys_user_has_group table for groups.  The examples that exist out there now where you just assign this field to a comma delimited list of strings does not work in Calgary.

3) Related to (2), keep in mind that membership of groups works the same way.  There is not a simple Reference. So, to add someone to a group or a role, you need to modify the correct membership table.

Code snippet from our implementation partner:

var roleToAssign = 'itil';
var groupToAssign = "Group Name";

if (!groupHasRole(groupToAssign, roleToAssign)){
   addRoleToGroup(groupToAssign, roleToAssign);
}

function groupHasRole(groupName, roleName){
   var groupRoleGR = new GlideRecord('sys_group_has_role');
   // We presume group and role exist
   groupRoleGR.addQuery('role.name', roleName);
   groupRoleGR.addQuery('group.name', groupName);
   groupRoleGR.query();
   return groupRoleGR.hasNext();
}

function addRoleToGroup(groupName, roleName){
   var newGroupRole = new GlideRecord('sys_group_has_role');
   newGroupRole.initialize();
   newGroupRole.role.setDisplayValue(roleName);
   newGroupRole.group.setDisplayValue(groupName);
   newGroupRole.insert();
}