The ServiceNow Nerd

The musings of a ServiceNow platform developer and enthusiast

The SN Nerd

Is Not Allowed In-Scope: The Only Workaround Guide You Need

by snnerd
Published: Last Updated on 5,040 views

This article was originally posted on the ServiceNow Community.

Contents

Introduction
Workaround for almost all Scoping Issues (The Last Resort)

GlideDateTime().setNumericValue()
GlideUser().getMyGroups()
gs.sleep()
gs.dateDiff()
GlideRecord().applyTemplate()
SncTriggerSynchronizer.executeNow()
GlideImpersonate().impersonate()
Bypassing Untouchable Before Query Rules
GlideEncryptor
GlideAjax XMLWait()

Introduction

JavaException: java.lang.SecurityException: XYZ is not allowed in scoped applications.

The bane of every ServiceNow Application Developers existence!

Some functions are not allowed in scope for a good reason ( like gs.getSession().impersonate() ) while others seem to be arbitrarily not available ( like applyTemplate() ).

If you want to know exactly what is and is not in callable in scope, it is all in the API documentation.

Scoped Server Side

Legacy Server Side

Working around almost all Scoping issues (The Last Resort)

You can work around all API limitations by creating a Global Script Include that is callable in all scopes containing all the global functions you need:

Why shouldn’t I just do this every time I see the dreaded not in scope error?

Because:

  • The Script Include cannot be packaged into your application
  • That feature cannot be included if you plan to publish to the App Store
  • Scoped applications cannot handle some objects that are returned from global API such as Java Arrays
  • Creating an Application in Scope forces you to make design decisions to minimize customization and reduce coupling
  • It is fun and oddly satisfying to find workarounds to these problems

Alas, it is best to minimize your dependency on custom global script includes.

This guideline will outline every workaround I have found to get around these limitations while staying in Scope!

GlideDateTime().setNumericValue()

var desiredMS = '176024386630'; // same as value in setNumericValue()
var dt = new GlideDateTime();
var ms = dt.getNumericValue();
dt.add(desiredMs - ms);

GlideUser().getMyGroups()

function getMyGroups() {
	var groups = [];
	var grmember = new GlideRecord('sys_user_grmember');
	grmember.addQuery('user', gs.getUserID());
	grmember.addQuery('group.active', true);
	grmember.query();
	while(grmember.next()){
		groups.push(grmember.getValue('group'));
	}
	// add all parent groups (this is global.getMyGroups behaviour)
	var children = groups;
	while(children.length > 0) {
		var grp = new GlideAggregate('sys_user_group');
		grp.addQuery('active', true);
		grp.addQuery('sys_id', 'IN', children.join(','));
		grp.addEncodedQuery('parentISNOTEMPTY');
		grp.groupBy('parent');
		grp.query();
		children = [];
		while(grp.next()) {
			var parent_id = grp.getValue('parent');
			if (groups.indexOf(parent_id) == -1) {
				groups.push(parent_id);
				children.push(parent_id);
			}
		}
	}
	return groups;
}

From user Tim2222 on this thread. This workaround respects the parent hierarchy.

gs.sleep()

function sleep(ms) {
  var endSleep = new GlideDuration().getNumericValue() + ms;
  while ( new GlideDuration().getNumericValue() < endSleep) {
   //wait 
  }

  return;
}

gs.dateDiff()

var gdtFutureDate = new GlideDateTime("2020-01-01 05:00:00");
var gdtPastDate = new GlideDateTime("2019-12-12 08:00:00"); 

// Convert from MS as required
var durationInMs = GlideDateTime.subtract(gdtFutureDate, gdtPastDate); 


GlideRecord().applyTemplate()

var sys_id_of_template = '56a8e507db6b26405accd5f0cf96190b';   
var grObj = new GlideRecord('incident');   
var t =   new GlideTemplate.get(sys_id_of_template);   
t.apply(grObj); 

This one is kind of complicated and is only a half-workaround.
In a Scoped Application, you can create Scheduled Jobs. Add the field ‘Run as’ to the form.

GlideImpersonate().impersonate()

Within the Script field, you can then evaluate any code you wish to as the user by using the GlideScopedEvaluator to point at any script field you have in the system (of the same application).

var evaluator = new GlideScopedEvaluator(); 
gr = new GlideRecord('x_app_table'); 
gr.addQuery('short_description','Testing GlideScopedEvaluator'); 
gr.query(); 
if (gr.next()) { 
    gs.info(evaluator.evaluateScript(gr, 'test_script', vars));
}

https://developer.servicenow.com/app.do#!/api_doc?v=madrid&id=r_ScopedGlideEvaluatorGlideScopedEvaluator

Update the Schedule Job ‘Run as’ field as the user you wish to impersonate.

Then, use GlideSystem.executeNow() in your code to run the Scheduled job.

SncTriggerSynchronizer.executeNow()

gs.executeNow(grScheduledJob);

While only a half workaround (only works server-side) it may still meet the requirement.

Bypassing Untouchable Before Query Rules 

There may be some Before Query rules that are hiding records that you need for your Scoped Application and from your Scope, you can’t touch them.
One example of these is the ‘user query’ Business Rule on the User table. It hides all inactive user records from non-admin. It is also untouchable from an Application Scope.

If you are building an Application for the Store or for other customers, you may not be in a position to modify this. Remember, changes to this rule cannot be packaged in your Scoped Application.

But you have a requirement for your Application to return some details about inactive Users!

What can you do?

Luckily for us, REST does not respect before query Business Rules.

Using the REST API explorer, you can write some code to use REST to get inactive User data.

var request = new sn_ws.RESTMessageV2();
var instanceURL = gs.getProperty('glide.servlet.uri');
var restQry = "api/now/table/sys_user?sysparm_query=active%3Dfalse&sysparm_limit=1";
request.setEndpoint(baseUrl+restQry); 
request.setHttpMethod('GET');

Credit to @jnovack for asking the question and @MB for providing the solution in this thread.

GlideEncryptor()

When working with encrypted fields, you can use the following code to decrypt and encrypt values:

// Use to decrypt a value
gr.password.getDecryptedValue(); 

//Use to encrypt a value
gr.password.setDisplayValue('un-encrypted string');

Credit to @sachin.namjoshi for providing the workaround in this thread.

GlideAjax XMLWait()

XMLWait() is not allowed in Application Scopes.

Sometimes you want to make Server side checks before allowing a record to submit.

This can be achieved using a design pattern by @Jon Barnes, cleverly utilizing the g_scratchpad variable to stop the form from submitting, then submitting the form when a result is retrieved (link below).

How To: Async GlideAjax in an onSubmit script

**

Post any of your workarounds in the comments below!

If you enjoyed this blog, please see here for my full series of ServiceKnow-How blogs!

Related Posts

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More