- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-19-2018 07:05 PM
So here's what I'm trying to do. I created a dynamic filter that calls a client-callable script include function to fetch a list of groups. Inside the script include I am referencing a table of rules to determine which groups meet a certain criteria. That rules table contains a script field that I'm evaluating using a GlideScopedEvaluator object in the script include.
Everything seems to work perfectly. If I execute the function in the script include in a background script, it works like a charm. However, when I try to use the dynamic filter, I'm seeing this error in my log:
org.mozilla.javascript.EcmaError: "GlideScopedEvaluator" is not defined.
Caused by error in sys_script_include.26ac96a90ffa934059a1db0be1050e17.script at line 124
!!? It looks like the GlideScopedEvaluator API isn't available when the script include function is called from a dynamic filter. I swear, if I copy the function call straight out of the Script field on my dynamic filter and into a background script, it returns an array of groups just like it's supposed to. But because a dynamic filter I suppose is considered a "client call", I'm getting the error above.
Anyone have any suggestions on how to get around this? It seems rather arbitrary to me that it simply would be impossible to use a rules table with scripts to determine which records are returned in a dynamic filter.
For reference, here's the exact code I'm running in the script include. An explanation follows...
GroupAggregation.getDynamicGroupMap = function(grAggregation) {
var groupMap = { };
if (!grAggregation) return groupMap;
if (!grAggregation.u_dynamic) return groupMap;
var encQuery = grAggregation.getValue('u_conditions');
var grGroup = new GlideRecord('sys_user_group');
if (encQuery) {
grGroup.addEncodedQuery(encQuery);
}
grGroup.query();
while (grGroup.next()) {
var addGroup = true;
if (grAggregation.u_use_condition_script) {
// Run the condition script
// This line triggers the error in the log
var evaluator = new GlideScopedEvaluator();
var answer;
evaluator.putVariable('answer', answer);
var result = evaluator.evaluateScript(grAggregation,
'u_condition_script', { current: grGroup });
answer = evaluator.getVariable('answer');
// If the answer variable was explicitly set, use its value
if (answer !== undefined) addGroup &= answer;
// Otherwise, use the value that was returned.
else if (result !== undefined) addGroup &= result;
// If nothing was set or returned, add the record
}
if (addGroup)
groupMap[grGroup.getValue('sys_id')] = true;
}
return groupMap;
};
GroupAggregation.getGroups = function(aggregation) {
var grAggregation = GroupAggregation.normalize(aggregation);
if (grAggregation) {
// Get dynamic groups
var groupMap = GroupAggregation.getDynamicGroupMap(grAggregation);
// Get static groups
var grM2m = new GlideRecord('u_m2m_group_aggregation');
grM2m.addQuery('u_group_aggregation', grAggregation.sys_id);
grM2m.query();
while (grM2m.next())
groupMap[grM2m.getValue('u_group')] = true;
return Object.keys(groupMap);
}
// If the aggregation passed in was not valid, return an empty set
return [ ];
};
So the dynamic filter contains a call to GroupAggregation.getGroups() with the sys_id of an aggregation of groups.
That function calls the GroupAggregation.getDynamicGroupMap() method, which checks the rules in the aggregation record to enumerate a list of groups. There are two parts to the rules: a condition builder (u_conditions) for simple rules, and a script field (u_condition_script) for more complicated, scripted rules. Both have to be true in order for a group to be added to the map that is returned to getGroups(). Once returned, the keys of that map are extracted into an array and returned to the caller, which will be a dynamic filter.
I've used this pattern several times with a lot of success, but this time I'm being thwarted by the error as noted in the code above. When it gets to the line create a new GlideScopedEvaluator, when run in the context of a dynamic filter, ServiceNow seems to think that such a thing doesn't exist. If I run this from a background script passing in the sys_id of a u_group_aggregation record, it works flawlessly.
Solved! Go to Solution.
- Labels:
-
Scripting and Coding
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-27-2018 10:15 AM
So I found an answer to this. It's kludgy, but it works. I wanted to post a follow-up in case anyone else is searching for an answer to this. In a nutshell, it boils down to: use eval. It's not ideal, and it's flagged by the syntax editor, but it does work.
GroupAggregation.getDynamicGroupMap = function(grAggregation) {
var groupMap = { };
if (!grAggregation) return groupMap;
if (!grAggregation.u_dynamic) return groupMap;
var encQuery = grAggregation.getValue('u_conditions');
var grGroup = new GlideRecord('sys_user_group');
if (encQuery) {
grGroup.addEncodedQuery(encQuery);
}
grGroup.query();
while (grGroup.next()) {
var addGroup = true;
if (grAggregation.u_use_condition_script) {
// Run the condition script
var script = 'var answer; var current = grGroup;';
script += grAggregation.getValue('u_condition_script');
// The next line is flagged by the editor, but it works.
var result = eval(script);
// If the answer variable was explicitly set, use its value
if (answer !== undefined) addGroup &= answer;
// Otherwise, use the value that was returned.
else if (result !== undefined) addGroup &= result;
// If nothing was set or returned, add the record
}
if (addGroup)
groupMap[grGroup.getValue('sys_id')] = true;
}
return groupMap;
};
GroupAggregation.getGroups = function(aggregation) {
var grAggregation = GroupAggregation.normalize(aggregation);
if (grAggregation) {
// Get dynamic groups
var groupMap = GroupAggregation.getDynamicGroupMap(grAggregation);
// Get static groups
var grM2m = new GlideRecord('u_m2m_group_aggregation');
grM2m.addQuery('u_group_aggregation', grAggregation.sys_id);
grM2m.query();
while (grM2m.next())
groupMap[grM2m.getValue('u_group')] = true;
return Object.keys(groupMap);
}
// If the aggregation passed in was not valid, return an empty set
return [ ];
};
Obviously, I wouldn't recommend this for general use, but as far as I can tell, it's the only way to run a script within a client-callable script.
Just as a matter of interest, I also tried creating a non-client-callable script with the GlideScopedEvalutor defined in it, and calling that non-client-callable script from the client-callable script. I didn't expect it to work, and sure enough, I had the same results as in my OP above.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-27-2018 10:15 AM
So I found an answer to this. It's kludgy, but it works. I wanted to post a follow-up in case anyone else is searching for an answer to this. In a nutshell, it boils down to: use eval. It's not ideal, and it's flagged by the syntax editor, but it does work.
GroupAggregation.getDynamicGroupMap = function(grAggregation) {
var groupMap = { };
if (!grAggregation) return groupMap;
if (!grAggregation.u_dynamic) return groupMap;
var encQuery = grAggregation.getValue('u_conditions');
var grGroup = new GlideRecord('sys_user_group');
if (encQuery) {
grGroup.addEncodedQuery(encQuery);
}
grGroup.query();
while (grGroup.next()) {
var addGroup = true;
if (grAggregation.u_use_condition_script) {
// Run the condition script
var script = 'var answer; var current = grGroup;';
script += grAggregation.getValue('u_condition_script');
// The next line is flagged by the editor, but it works.
var result = eval(script);
// If the answer variable was explicitly set, use its value
if (answer !== undefined) addGroup &= answer;
// Otherwise, use the value that was returned.
else if (result !== undefined) addGroup &= result;
// If nothing was set or returned, add the record
}
if (addGroup)
groupMap[grGroup.getValue('sys_id')] = true;
}
return groupMap;
};
GroupAggregation.getGroups = function(aggregation) {
var grAggregation = GroupAggregation.normalize(aggregation);
if (grAggregation) {
// Get dynamic groups
var groupMap = GroupAggregation.getDynamicGroupMap(grAggregation);
// Get static groups
var grM2m = new GlideRecord('u_m2m_group_aggregation');
grM2m.addQuery('u_group_aggregation', grAggregation.sys_id);
grM2m.query();
while (grM2m.next())
groupMap[grM2m.getValue('u_group')] = true;
return Object.keys(groupMap);
}
// If the aggregation passed in was not valid, return an empty set
return [ ];
};
Obviously, I wouldn't recommend this for general use, but as far as I can tell, it's the only way to run a script within a client-callable script.
Just as a matter of interest, I also tried creating a non-client-callable script with the GlideScopedEvalutor defined in it, and calling that non-client-callable script from the client-callable script. I didn't expect it to work, and sure enough, I had the same results as in my OP above.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-04-2018 04:05 PM
Running into something similar where GlideScopedEvaluator doesn't seem to be working in a business rule. It works fine in workflows and background scripts, but absolutely nothing happens in business rules. Wish this was documented better as the only other option is to use eval.