The Now Platform® Washington DC release is live. Watch now!

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
The SN Nerd
Mega Sage
Mega Sage

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.

 find_real_file.png

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:

find_real_file.png

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); 

 

GlideImpersonate().impersonate()

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.

find_real_file.png

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_ScopedGlideEvaluatorGlideScopedEvalu...

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.

find_real_file.png

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!

3 Comments