- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-27-2019 02:16 PM
I'm trying to prevent an auto approval of a service catalog request item from happening in a workflow if the approver isn't defined. After scouring the various communities I came up on this nugget from Obi Wan Tomasi in which he describes very nearly my exact use case -- to wit: "For example, you say 'Chuck's Manager needs to approve this' and I have no manager defined. ". So I put an IF activity upstream of the Approval-User activity, to head off the approval if there wasn't anyone to approve it, and added the following script.
// This script needs to set answer to 'yes' or 'no' to indicate the state of the activity.
//
// For example,
//
// answer = ifScript();
//
// function ifScript() {
// if (condition is true) {
// return 'yes';
// }
// return 'no';
// }
answer = ifScript();
function ifScript() {
var approvers = [];
// Get the sys_id of the Data Coordinators group
var dcRec = new GlideRecord('sys_user_group');
var isValidRec = dcRec.get('name','Data Coordinators');
var dcGrpId = dcRec.sys_id;
// Get the sys ID for the SO
var ncccsRec = new GlideRecord('core_company');
isValidRec = ncccsRec.get('name', 'NCCCS');
var ncccsID = ncccsRec.sys_id;
// Get the company of the requested-for user
var reqForCo = current.request.requested_for.company;
// if the current requested for user is an NCCCS employee, get the direct manager
if (reqForCo == ncccsID) {
approvers.push(current.request.requested_for.manager);
} else {
// If the requested for user is a college employee, get the Data Coordinator(s) for the college
var dcMemberRec = new GlideRecord('sys_user_grmember');
dcMemberRec.addQuery('group','=',dcGrpId);
dcMemberRec.addQuery('user.company','=',reqForCo);
dcMemberRec.query();
// Add each user retrieved to the list to return
while (dcMemberRec.next()){
approvers.push(dcMemberRec.sys_id);
}
}
if (approvers.length > 0) {
return 'yes';
} else {
return 'no';
}
}
Obviously it didn't work, but I can't sort out why not. What it does is blow through the IF, as if it never executes the script. And I'm inclined to think that's precisely what's happening, because even when I had liberally applied gs.log and workflow.debug, none of my messages turned up in the log. Odder still, I had used almost exactly this same logic in the additional approver script and it worked perfectly for identifying who to assign the approval to, so I'm reasonably sure I haven't run too far into the ditch.
I'm still pretty much a rookie with workflows, but this one is seemed fairly simple:
The IF is supposed to take the process out through a set values activity that cancels the request, and a notification that explains why, and exit gracefully. What's ACTUALLY happening is that it's going right past the if, straight into the auto approve, and creating the SC Task and waiting for someone to complete the task and close out the request.
For what it's worth, I had the process working until I started trying to validate the approver(s) before entering the approval user activity. And it should STILL work if there's a manager to approve it, but the branch that should take it down the off ramp is failing spectacularly for reasons unknown.
Looking through some of the OOB workflows, I don't find a lot of IF activities used, but it seemed like the most logical choice for what I'm trying to do. And my guess is that it probably is the best choice -- once I figure out what magic incantation I need to use to make it work.
All suggestions are welcome, including which logging methods to use in workflow scripts. I tried both gs.log and workflow.debug, and got bupkes from both of them. But I don't know if that was because I was trying to use the wrong methods or because the script never executed, or because it failed somewhere else without throwing an error. I did see a warning for an invalid query in the log, but I couldn't tell if it was my script that generated it or something else that was running in another session. The message didn't give any details about what the offending query was, but it was apparently flagged by sys_query_evaluator (I think that was what it was called anyway) which suggests that if that's what was causing the problem, it basically took the script out of play before execution even started, which now I think of it would explain the behavior I'm seeing. Maybe. I'm guessing, because that's all I got at this point. I'm hoping the ServiceNow Jedi Council has some wisdom to impart.
2019-09-27 16:22:04
| Warning | Invalid query detected, please check logs for details [Unknown field in table sys_script_validator] | QueryEventLogger |
Solved! Go to Solution.
- Labels:
-
Scripting and Coding
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-30-2019 07:32 AM
UPDATE (1): Correction... it should be .nil() not .nil. smh.
UPDATE (2): So now I have the script working, but it seems the workflow itself won't execute for certain users. That's another question though, so I"m calling this one a win while I still can.
Well there was some instructional value here. I replaced the Glide queries with sys_ids, and put back the gs.logs I was using before, and I got better information (which is to say I got some information) but it's still not behaving as expected. This is the modified script:
answer = ifScript();
function ifScript() {
gs.log('JAB: Entering Approver Exists ifScript'); // for troubleshooting purposes
var approvers = [];
// Get the sys_id of the Data Coordinators group
// var dcRec = new GlideRecord('sys_user_group');
// var isValidRec = dcRec.get('name','Data Coordinators');
// var dcGrpId = dcRec.sys_id;
var dcGrpId = '63c2d380dbdc84508f75c191159619bb'; // for troubleshooting purposes
// Get the sys ID for the SO
// var ncccsRec = new GlideRecord('core_company');
// isValidRec = ncccsRec.get('name', 'NCCCS');
// var ncccsID = ncccsRec.sys_id;
var ncccsID = '6990f8f4db035340a0cc4b8b0b961949'; // for troubleshooting purposes
// Get the company of the requested-for user
var reqForCo = current.request.requested_for.company;
gs.log('JAB: reqForCo = ' + reqForCo); // for troubleshooting purposes
// if the current requested for user is an NCCCS employee, get the direct manager
if (reqForCo == ncccsID) {
gs.log('JAB: NCCCS Employee'); // for troubleshooting purposes
approvers.push(current.request.requested_for.manager);
gs.log('JAB: Approvers = ' + approvers); // for troubleshooting purposes
} else {
// If the requested for user is a college employee, get the Data Coordinator(s) for the college
gs.log('JAB: College employee'); // for troubleshooting purposes
var dcMemberRec = new GlideRecord('sys_user_grmember');
dcMemberRec.addQuery('group','=',dcGrpId);
dcMemberRec.addQuery('user.company','=',reqForCo);
dcMemberRec.query();
gs.log('JAB: Group memebr query returns: ' + dcMemberRec.getRowCount() + ' records'); // for troubleshooting purposes
// Add each user retrieved to the list to return
while (dcMemberRec.next()){
approvers.push(dcMemberRec.sys_id);
}
}
gs.log('JAB: Approvers = ' + approvers + ' has ' + approvers.length + ' member(s)'); // for troubleshooting purposes
if (approvers.length > 0) {
gs.log('Return yes');
return 'yes';
} else {
gs.log('Return no');
return 'no';
}
}
And these are the log statements that were generated:
2019-09-30 09:21:41 Information JAB: Approvers = *** Script
2019-09-30 09:21:41 Information JAB: NCCCS Employee *** Script
2019-09-30 09:21:41 Information Return yes *** Script
2019-09-30 09:21:41 Information JAB: reqForCo = 6990f8f4db035340a0cc4b8b0b961949 *** Script
2019-09-30 09:21:41 Information JAB: Approvers = has 1 member(s) *** Script
2019-09-30 09:21:41 Information JAB: Entering Approver Exists ifScript *** Script
2019-09-30 09:21:41 Information Workflow starting: ESS Data Warehouse Access Request, for RITM0010223 ENGINE
2019-09-30 09:21:40 Information DESIRED = in_process *** Script
2019-09-30 09:21:40 Information CURRENT STAGE = requested *** Script
2019-09-30 09:21:40 Information terminating Service Catalog Request, state: finished ENGINE
2019-09-30 09:21:40 Information APPROVAL = approved CHANGES = true *** Script
2019-09-30 09:21:40 Information Workflow starting: Service Catalog Request, for REQ0010220 ENGINE
2019-09-30 09:21:33 Information Email Digest complete processed=0 error=0 elapsedms=1 EmailDigest
2019-09-30 09:21:24 Warning Invalid query detected, please check logs for details [Unknown field in table sys_script_validator] QueryEventLogger
Note the bolded line where I'm told that an empty array has 1 element in it
gs.log('JAB: Approvers = ' + approvers + ' has ' + approvers.length + ' member(s)');
As a consequence of this impossibility, I'm being sent down the Yes branch of the If, which routes me to the Approval User activity, which in turn triggers an auto approval because there is no manager. Which is the very problem I was trying to solve to begin with.
Since by every definition of Javascript I know, and empty array cannot have a length greater than zero, I am forced to conclude that the array is not, in fact, empty and that pushing an empty string does in fact add an element to the array -- it's just an "invisible" one.
Testing this theory in a node session on my laptop without the overhead of SN, like so:
var approvers = [];
var dummy = '';
console.log('Approvers = [' + approvers + '], has ' + approvers.length + ' members');
approvers.push(dummy);
console.log('(post-push) Approvers = [' + approvers + '], has ' + approvers.length + ' members');
I get the following results:
Approvers = [], has 0 members
(post-push) Approvers = [], has 1 members
if (!current.request.requested_for.manager.nil) {
approvers.push(current.request.requested_for.manager);
gs.log('JAB: Approvers = ' + approvers); // for troubleshooting purposes
} else {
gs.log('No manager present, cannot approve');
}
which should prevent the "ghost approver" from mucking things up.
Annnd... here's the money shot...
2019-09-30 10:10:15 Information No manager present, cannot approve *** Script
and the workflow sets the requested item to canceled, sends a notification saying "Sorry, but thanks for playing" and exits gracefully without triggering an auto approval for access to a system we intend to require approval for.
Now to see about those system properties...

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-28-2019 07:25 AM
If this were me, I'd replace the first few lines with a sysID vs the Glide queries. I'd actually create a system property and then call that but for testing I just hard-coded 2 sysID from my developers instance. Try replacing those sysIDs from your instance and then check the system log. If they belong to NCCCS, you should see that in system log. If they are part of Data Coordinators, you should see that in system log. Otherwise the If should be No.
answer = ifScript();
function ifScript() {
var approvers = [];
// Get the sys_id of the Data Coordinators group
// var dcRec = new GlideRecord('sys_user_group');
// var isValidRec = dcRec.get('name', 'Data Coordinators');
var dcGrpId = 'db53580b0a0a0a6501aa37c294a2ba6b';
// Get the sys ID for the SO
// var ncccsRec = new GlideRecord('core_company');
// isValidRec = ncccsRec.get('name', 'NCCCS');
var ncccsID = 'df7d53303710200044e0bfc8bcbe5dac';
// Get the company of the requested-for user
var reqForCo = current.request.requested_for.company;
gs.log('what is reqForCo = ' + reqForCo);
// if the current requested for user is an NCCCS employee, get the direct manager
if (reqForCo == ncccsID) {
gs.log('what is NCCCS employee');
approvers.push(current.request.requested_for.manager);
} else {
// If the requested for user is a college employee, get the Data Coordinator(s) for the college
var dcMemberRec = new GlideRecord('sys_user_grmember');
dcMemberRec.addQuery('group', '=', dcGrpId);
dcMemberRec.addQuery('user.company', '=', reqForCo);
dcMemberRec.query();
// Add each user retrieved to the list to return
while (dcMemberRec.next()) {
gs.log('what is Data Coordinator');
approvers.push(dcMemberRec.sys_id);
}
}
if (approvers.length > 0) {
return 'yes';
} else {
return 'no';
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-30-2019 07:32 AM
UPDATE (1): Correction... it should be .nil() not .nil. smh.
UPDATE (2): So now I have the script working, but it seems the workflow itself won't execute for certain users. That's another question though, so I"m calling this one a win while I still can.
Well there was some instructional value here. I replaced the Glide queries with sys_ids, and put back the gs.logs I was using before, and I got better information (which is to say I got some information) but it's still not behaving as expected. This is the modified script:
answer = ifScript();
function ifScript() {
gs.log('JAB: Entering Approver Exists ifScript'); // for troubleshooting purposes
var approvers = [];
// Get the sys_id of the Data Coordinators group
// var dcRec = new GlideRecord('sys_user_group');
// var isValidRec = dcRec.get('name','Data Coordinators');
// var dcGrpId = dcRec.sys_id;
var dcGrpId = '63c2d380dbdc84508f75c191159619bb'; // for troubleshooting purposes
// Get the sys ID for the SO
// var ncccsRec = new GlideRecord('core_company');
// isValidRec = ncccsRec.get('name', 'NCCCS');
// var ncccsID = ncccsRec.sys_id;
var ncccsID = '6990f8f4db035340a0cc4b8b0b961949'; // for troubleshooting purposes
// Get the company of the requested-for user
var reqForCo = current.request.requested_for.company;
gs.log('JAB: reqForCo = ' + reqForCo); // for troubleshooting purposes
// if the current requested for user is an NCCCS employee, get the direct manager
if (reqForCo == ncccsID) {
gs.log('JAB: NCCCS Employee'); // for troubleshooting purposes
approvers.push(current.request.requested_for.manager);
gs.log('JAB: Approvers = ' + approvers); // for troubleshooting purposes
} else {
// If the requested for user is a college employee, get the Data Coordinator(s) for the college
gs.log('JAB: College employee'); // for troubleshooting purposes
var dcMemberRec = new GlideRecord('sys_user_grmember');
dcMemberRec.addQuery('group','=',dcGrpId);
dcMemberRec.addQuery('user.company','=',reqForCo);
dcMemberRec.query();
gs.log('JAB: Group memebr query returns: ' + dcMemberRec.getRowCount() + ' records'); // for troubleshooting purposes
// Add each user retrieved to the list to return
while (dcMemberRec.next()){
approvers.push(dcMemberRec.sys_id);
}
}
gs.log('JAB: Approvers = ' + approvers + ' has ' + approvers.length + ' member(s)'); // for troubleshooting purposes
if (approvers.length > 0) {
gs.log('Return yes');
return 'yes';
} else {
gs.log('Return no');
return 'no';
}
}
And these are the log statements that were generated:
2019-09-30 09:21:41 Information JAB: Approvers = *** Script
2019-09-30 09:21:41 Information JAB: NCCCS Employee *** Script
2019-09-30 09:21:41 Information Return yes *** Script
2019-09-30 09:21:41 Information JAB: reqForCo = 6990f8f4db035340a0cc4b8b0b961949 *** Script
2019-09-30 09:21:41 Information JAB: Approvers = has 1 member(s) *** Script
2019-09-30 09:21:41 Information JAB: Entering Approver Exists ifScript *** Script
2019-09-30 09:21:41 Information Workflow starting: ESS Data Warehouse Access Request, for RITM0010223 ENGINE
2019-09-30 09:21:40 Information DESIRED = in_process *** Script
2019-09-30 09:21:40 Information CURRENT STAGE = requested *** Script
2019-09-30 09:21:40 Information terminating Service Catalog Request, state: finished ENGINE
2019-09-30 09:21:40 Information APPROVAL = approved CHANGES = true *** Script
2019-09-30 09:21:40 Information Workflow starting: Service Catalog Request, for REQ0010220 ENGINE
2019-09-30 09:21:33 Information Email Digest complete processed=0 error=0 elapsedms=1 EmailDigest
2019-09-30 09:21:24 Warning Invalid query detected, please check logs for details [Unknown field in table sys_script_validator] QueryEventLogger
Note the bolded line where I'm told that an empty array has 1 element in it
gs.log('JAB: Approvers = ' + approvers + ' has ' + approvers.length + ' member(s)');
As a consequence of this impossibility, I'm being sent down the Yes branch of the If, which routes me to the Approval User activity, which in turn triggers an auto approval because there is no manager. Which is the very problem I was trying to solve to begin with.
Since by every definition of Javascript I know, and empty array cannot have a length greater than zero, I am forced to conclude that the array is not, in fact, empty and that pushing an empty string does in fact add an element to the array -- it's just an "invisible" one.
Testing this theory in a node session on my laptop without the overhead of SN, like so:
var approvers = [];
var dummy = '';
console.log('Approvers = [' + approvers + '], has ' + approvers.length + ' members');
approvers.push(dummy);
console.log('(post-push) Approvers = [' + approvers + '], has ' + approvers.length + ' members');
I get the following results:
Approvers = [], has 0 members
(post-push) Approvers = [], has 1 members
if (!current.request.requested_for.manager.nil) {
approvers.push(current.request.requested_for.manager);
gs.log('JAB: Approvers = ' + approvers); // for troubleshooting purposes
} else {
gs.log('No manager present, cannot approve');
}
which should prevent the "ghost approver" from mucking things up.
Annnd... here's the money shot...
2019-09-30 10:10:15 Information No manager present, cannot approve *** Script
and the workflow sets the requested item to canceled, sends a notification saying "Sorry, but thanks for playing" and exits gracefully without triggering an auto approval for access to a system we intend to require approval for.
Now to see about those system properties...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-30-2019 01:02 PM
If i understand it correctly, your script above runs perfectly when it is used inside Approver-User activity as "Advanced Additional Approvers script". If thats the case, then you should just add additional condition to your activity. If you double-click the "Approved" condition on the activity, you will see the result is set as "activity.result == 'approved' || activity.result == 'skipped'". Divide them into different conditions results on the activity and for skipped --> route it to the notification you want.
eg:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-03-2019 05:37 AM
Turned out to be a simple Javascript brain cramp that was causing the problem. Why it didn't come up when I was using the same logic in the approval user activity is anybody's guess, but what was happening was I had an array that appeared to be empty that actually wasn't. Which was triggering a "Yes" condition in the If activity, which was then passing the approval along and with no actual approver available, the auto-approve that workflow does (which is kind of terrifying IMO) was taking over and the request item was being approved automatically.
But given that Workflow is a little bit opaque and the documentation on it can be a bit ... "dense" this will probably be useful information to have at some future point. This wasn't the last problem I ran up against, but I'm getting a crash course in how this all fits together as a result. Thanks for adding to the syllabus!