Jacob Kimball, Author at ServiceNow Guru https://servicenowguru.com/author/jacobkimball/ ServiceNow Consulting Scripting Administration Development Thu, 05 Dec 2024 19:40:55 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.2 https://servicenowguru.com/wp-content/uploads/2024/05/cropped-SNGuru-Icon-32x32.png Jacob Kimball, Author at ServiceNow Guru https://servicenowguru.com/author/jacobkimball/ 32 32 Implementing a Factory Pattern on ServiceNow https://servicenowguru.com/scripting/implementing-a-factory-pattern-on-servicenow/ Thu, 05 Dec 2024 19:40:55 +0000 https://servicenowguru.com/?p=16592 From time to time I've run into a situation where I need to branch to use a different set of code depending on some attribute or circumstance, but regardless of the branch the code is essentially doing the same thing, it just has to do it a different way. To put more of a point

The post Implementing a Factory Pattern on ServiceNow appeared first on ServiceNow Guru.

]]>
From time to time I’ve run into a situation where I need to branch to use a different set of code depending on some attribute or circumstance, but regardless of the branch the code is essentially doing the same thing, it just has to do it a different way. To put more of a point on this, imagine that you need to integrate to update an incident on another system…but it’s not just one other system, it’s potentially many and they are all different vendors. In the end, you need to update a record on a target system, but how you do that is most likely different as each system will have its own API to consume to do this. One way to accomplish it would be to build an if/then or switch block with each block calling the right code to interact with the other system. But another, more flexible way, is to build a factory that dynamically gives you the code you need based on attributes you have defined in your instance.

The factory analogy is apt here and is a fairly common pattern in programming.  At a high level:

  • Factory: The factory represents a central machine that produces different items.
  • Products: The items on the conveyor belt are the products created by the factory.
  • Clients: The workers who take items off the conveyor belt are the clients using the factory.

There are a couple of key components to this technique, and we’re going to walk through them. As we work through an example, we will tie what we build to the above concepts to connect the dots!

First, let’s simplify and refine the above general example. We have two external systems we need to integrate with to create/update incidents. Each external system has its own REST API to call with a unique payload. We know which system we have to integrate with by the company associated with the user caller attribute on the incident in our system.  There are 2 companies right now, CompanyOne & CompanyTwo, that we need to integrate with.

At a high level, the steps involved in creating our factory are:

  1. Create the code that would call each company’s API correctly.
  2. Create a blank/dummy script include (this will be our factory).
  3. Get GlideRecord reference to our factory.
  4. Overwrite the script value from 3 with the string that represents one of our script includes from step 1.
  5. Run the GlideRecord from 4 through GlideScriptEvaluator to get an object.

Now in more detail….

Step 1

Create script includes that call the respective API’s. As a standard we’ll refer to those script includes as CompanyOneAPI & CompanyTwoAPI. These script includes contain the unique logic needed to consume the respective company’s API. So far, so good…nothing really ‘new’, right?  Here’s the new part. In each script, the method that is called to fire things off is named the same. Something like ‘execute’. So to start the integration in each case would be:

new CompanyOneAPI().execute()

or

new CompanyTwoAPI().execute()

Step 2

Now create a ‘dummy’ script include. You can name it anything, but something descriptive is always recommended and helpful. The script include does NOT need to have any actual script in it, but you can take the default scripting given as part of creating a new script include. This script include will serve our ‘factory’. It takes some input and will give us a ‘product’. Here’s an example:

Name: Factory

Description: Just used by "factories" as a record for dynamically creating other objects.

Script:

var Factory = Class.create();
Factory.prototype = {
     /*
     This script include is used purely as a placeholder to build other objects for instantiation. It is referenced in other script includes and the script attribute is 'written' too but never saved. That 
     script is then executed to dynamically give the object that is needed based on the attributes passed in the the "factory".
     */
     initialize: function() {},
     type: 'Factory'
};

 

Step 3

Now that we have our “factory” script include we need to get a GlideRecord to our Factory script include:

var grFactory = newGlideRecord("sys_script_include");
grFactory.get("api_name", "Factory");

Step 4

Next, set the script value of the script include from step 3 to the name of one of the script includes that does the actual integration (created in step one):
grFactory.script = "CompanyOneAPI";
Notes:
  • If “CompanyOneAPI” is in a specific scope, that scope would be included as well (I.e. <scope_name>.CompanyOneAPI).
  • We do NOT ever save ( .update() ) the grFactory GlideRecord.

Step 5

Finally, you take the grFactory and run it through GlideScopedEvaluator to get an instantiated script, and then an object:

var evaluator = new GlideScopedEvaluator();
var model = evaluator.evaluateScript(grFactory, 'script');
var handler = new model();
”handler” can now be used as if you’d done this:
new CompanyOneAPI();
This gives us our ‘product’ from ‘factory’.

Putting it all together

Why go through all the hassle above?  Take a look at step 4 above again.  We explicitly set the script to be “CompanyOneAPI”…but what if we wrapped steps 3-5 in a utility and allowed the script string we want to be instantiated by being passed in as an attribute?  Something like:

getInstance = function( script_include_name ) {
     //Get a reference to the script include created for populating it's script field with the name of the script include passed in.
     var grFactory = newGlideRecord("sys_script_include");
     grFactory.get("api_name", "x_snc_trng_util.Factory");
     grFactory.script = script_include;
     //Use GlideScopedEvaluator to turn evaluate the script above, returning an instantiated handler that is returned.
     var evaluator = newGlideScopedEvaluator();
     var model = evaluator.evaluateScript(grFactory, 'script');
     var handler = new model();
     return handler;
};
Now we can pass any script include name and get an instance of it.  Couple this up with a data model that allows us to look up the right script include based on the company that it is for and we have the foundation for something truly flexible going forward as company3,4,… gets added. To do the lookup there are a couple of obvious approaches:
  • Add an attribute to core_company to contain the script include string
  • Add a look-up table that ties a reference to the company to the script include string (and possibly some other attribute for further delineation).

As we’ve gone the path of the second option for most of our implementations, we’ll look at that one at a high level:

 

Company (Ref) Attribute (Str) Script (Str)
CompanyA integration.incident CompanyOneAPI()
CompanyB integration.incident CompanyTwoAPI()

 

Now we have a table we can use to look up the specific API script for a company. In our example the company would come from the caller on the incident.  We would use that in the look-up table to find the right API to use:

var LookUpGr = new GlideRecord("LookupTable");
LookUpGr.addEncodedQuery("company=<company_sys_id>^attribute=integration.incident");
LookUpGr.query();

Then, assuming we found a look-up record we could take the script string and create our handle:

if (LookUpGr.next) { 
     var data = {<JSON object with info needed for API call>}
     //Get an instantiated javascript object
     var handler = getInstance(LookUpGr.getValue('script')); 
     //Take that object and run it's execute method.  Each company API should have the same method to call (i.e. execute)
     var response = handler.execute(data); //evaluate response 
}

The last line essentially being the ‘client’ that takes the ‘product’ from the factory and uses it.

Now that we have the above framework, when we get the next company that we need to integrate with we’d need to:

  1. Create CompanyThreeAPI (with an execute method) that calls the other systems API to create/update incidents.
  2. Create a mapping in the look-up table for CompanyThree

Hopefully the above is a pattern you can find useful as I am sure there are many other applications where it can be applied. Please let me know what you think or if there’re any questions!

The post Implementing a Factory Pattern on ServiceNow appeared first on ServiceNow Guru.

]]>
Leveraging Joins in GlideRecord Queries https://servicenowguru.com/scripting/leveraging-joins-gliderecord-queries/ https://servicenowguru.com/scripting/leveraging-joins-gliderecord-queries/#comments Thu, 01 Aug 2024 17:33:11 +0000 https://servicenowguru.com/?p=16590 A relatively common scenario in ServiceNow is to have to use the results from one query to filter conditions for another query.  This technique is commonly referred to as using a subquery. In general the scenario will be something like this: You need a set of information from a table. That information should be filtered

The post Leveraging Joins in GlideRecord Queries appeared first on ServiceNow Guru.

]]>
A relatively common scenario in ServiceNow is to have to use the results from one query to filter conditions for another query.  This technique is commonly referred to as using a subquery. In general the scenario will be something like this:

  • You need a set of information from a table.
  • That information should be filtered based on information from another table that is related…but the relationship/reference that connects them is pointing the wrong direction to use dot-walking.
  • You do a query of the first table, loop through and assemble the attributes you want to use as additional filtering (in an array typically).
  • Use the results as part of the conditions on the query against another table.

Something like this:

var active_inc_array = []; 
var IncGr = new GlideRecord("incident"); 
IncGr.addEncodedQuery('active=true^problem_idISNOTEMPTY'); 
IncGr.query(); 
while(IncGr.next()) { 
     active_inc_array.push(IncGr.problem_id + ""); 
} 
var ProbGr = new GlideRecord("problem"); 
ProbGr.addQuery("active","false"); 
ProbGr.addQuery("sys_id", "IN", active_inc_array.join()); 
ProbGr.query();

In the above we ultimately want information from the problem table, however, we want to use incident information to filter. To achieve this we first query the incident table for all active incidents that reference a problem record, create an array of the problem record sys_id’s then use that problem sys_id array as filtering against the problem table along with the active=false.  The idea here being we probably shouldn’t have active incidents if the problem the incident is related too is not active.

While the above works fine, there is a better, more performant, cleaner way of accomplishing the same result. That way is by using a join on you GlideRecord query. The word join is a bit misleading as it is not really a join as you might have experienced when building/using a database view.  The join is really a subquery as it helps you filter results from one table based on data in another table BUT you can NOT access data in that other table as part of the record set returned from your GliderRecord call.

Lets take a look at our previous example and then look at how we can do it better. First, our ‘brute force’ method again:

Brute Force

var active_inc_array = [];
var IncGr = new GlideRecord("incident");
IncGr.addEncodedQuery('active=true^problem_idISNOTEMPTY'); 
IncGr.query();
while(IncGr.next()) {
     active_inc_array.push(IncGr.problem_id + "");
}
var ProbGr = new GlideRecord("problem");
ProbGr.addQuery("active","false");
ProbGr.addQuery("sys_id", "IN", active_inc_array.join());
ProbGr.query();

To do this cleaner/better we can use the join functionality that is part of GlideRecord. GlideRecord presents a couple of ways to use join functionality.  First is through the use of the addJoinQuery() function. The addJoinQuery() function takes a table name as it’s parameter. In our case ‘incident’. Take a look at our use case implemented using addJoinQuery():

GlideRecord.addJoinQuery()

var ProbGr = new GlideRecord('problem');
var IncJoin = ProbGr.addJoinQuery('incident');
ProbGr.addQuery('active', 'false');
IncJoin.addCondition('active', 'true');
ProbGr.query();

Note a couple of things:

  • You must assign the result of the addJoinQuery() to a variable as you then need to reference that variable later for the condition.
  • There has to be a reference on the subquery table (in our example incident, through the field problem_id) to the ‘main’ table (in our example problem).

The above works but also has a bit of black box ‘magic’ involved as you don’t explicitly get to define how the two tables are connected/’joined’. To remove that ambiguity you can also do the same using an encoded query string:

Encoded Query

var query = "active=false^JOINproblem.sys_id=incident.problem_id!active=true"
var ProbGr = new GlideRecord('problem'); 
ProbGr.addEncodedQuery(query); 
ProbGr.query();

Lets take a look at the encoded query string in line 1 a bit closer as that is where the fun is:) Deconstructing the string:

  • active=false: conditions on the target table, in our case problem
  • JOINproblem.sys_id=incident.problem_id:  explicitly definition of the criteria that connects records from the target table (problem) to the subquery table (incident) using the JOIN keyword
  • !active=true: condition on the subquery table, in our case incident, following the “!” delimter

The one limitation that we have run in to is that the condition on the subquery table only allows 1 condition to be applied (in our case active=true).

The above example is obviously a bit trivial but for larger data sets I’ve seen a noticeable difference in performance between the brute force method and using GlideRecord JOINs. Given how often I have run in to the need to use a subquery, I’ve found the usage of the GlideRecord JOIN functionality a much more efficient alternative to have in my tool box. Hopefully you will as well!

The post Leveraging Joins in GlideRecord Queries appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/scripting/leveraging-joins-gliderecord-queries/feed/ 5
Leveraging User Criteria in your custom applications https://servicenowguru.com/scripting/leveraging-user-criteria-custom-applications/ https://servicenowguru.com/scripting/leveraging-user-criteria-custom-applications/#comments Mon, 03 Jun 2024 12:44:36 +0000 https://servicenowguru.com/?p=15374 User Criteria (UC) have been around on the ServiceNow platform for quite some time now. They were a welcome extension to providing a more flexible way for controlling the set of users who should or shouldn’t have access to specific records. With UCs you can define a grouping of users either by name, groups they

The post Leveraging User Criteria in your custom applications appeared first on ServiceNow Guru.

]]>
User Criteria (UC) have been around on the ServiceNow platform for quite some time now. They were a welcome extension to providing a more flexible way for controlling the set of users who should or shouldn’t have access to specific records. With UCs you can define a grouping of users either by name, groups they belong to, roles they have, or through a script.

Standard User Criteria definition form.

You can then take that UC and add/configure it on a record as having the ability to ‘can read’ or ‘cannot read’ a specific record or group of records. Probably the best known examples of this are in the OOB knowledge base and service catalog applications.

KB article can read/cannot read attributes.

Available For/Not Available For related lists on catalog items.

But what if you wanted to be able to use UCs in your custom applications? The following will provide a roadmap on how you can do this!

Lets review how UCs are used for security by using the concrete example from the knowledge base. For the Knowledge Base application there are can/cannot read attributes (KB article image shown above) on the knowledge base record as well as the individual article record.  On the knowledge base record the UCs are associated as related records and shown in the ‘Can Read’/’Cannot Read’ related lists.  On the knowledge base articles there are ‘Can Read’/’Cannot Read’ glidelist attributes directly on the record. The way the OOB security has been implemented to use those UC lists for determining if a user can read a specific knowledge article is that:

  • A user can NOT be in either the knowledge base or knowledge articles cannot read UCs to be able to read the article.
  • If there is a can read UC on the knowledge base, the user must be in the UC to read articles in the knowledge base.
  • If there is a can read UC on the knowledge article, the user must be in the UC to read the article.
  • If there are no UCs defined on either the knowledge base or article, the article is readable by any user.

This is generally the way the service catalog works as well with the catalog and catalog item.

There are other OOB applications using UCs as well….but what if you wanted to be able to use UCs in other places to control access that aren’t part of OOB applications? To do this you’d need some way of evaluating a user’s inclusion in a UC. Thankfully there is a scriptable object that does just that: UserCriteriaLoader.

UserCriteriaLoader was a utility Mike Sherman (also a long-time ServiceNow employee) and I found while trying to develop the security model for a project we were working on. It’s relatively easy to use and just needs some fairly straightforward wrappers to make it reusable for our needs.

UserCriteriaLoader has several functions on it but the main one we are going to make use of is userMatches(). The userMatches() function takes the sys_id of a user you’re checking and an array of UC sys_id’s to check against. The function then evaluates the user against those UCs and returns true/false indicating if the user is in at least one of the UCs. Something like the following:

var user = gs.getUserId();
var uc_array = [“<uc_sys_id_1>”, “<uc_sys_id_2>”, “<uc_sys_id_3>”];
var ret = sn_uc.UserCriteriaLoader.userMatches( user, uc_array);


The above is a great start as it gives you the base capability to evaluate a user’s inclusion in a set of UCs, but what we really want is a utility that wrappers the above to give us a generic way of evaluating users against UCs in the way that UCs tend to be applied to records.

The script include below gives us a reusable utility to do this in the two primary ways we found UCs to be applied to a record:

  • A glidelist directly on the record
  • A related list with references to the record and to UC’s


UserCriteriaLoader.userMatches() requires the user’s sys_user record sys_id and an array of UC record sys_ids. The ‘trick’ to the utility is converting either the glidelist or a list of related records into that array of UC record sys_ids. Let’s look at some specific examples of how to use each function.

//'UserCriteriaLoader.userMatches()' Sample Usage
var gr = new GlideRecord("my_custom_table"); //Table name of record for which are evaluating access
gr.get(""); //sys_id of record for which you are evaluating access
var user_can_read = new UserCriteria(gs.getUserID(), gr).evalUCGlideList("my_can_read", "my_cannot_read");

The above would return true/false depending on the user’s inclusion in the can/cannot read UCs. A couple of things to note:

  • The cannot read parameter on the function is optional.  If not included it is assumed that the user is NOT part of any cannot read UCs.
  • If given a cannot read parameter, the function will take it into account by determining if the given user is part of the cannot read UC(s) and if they are returning false as the ‘answer’ as being included in any can read UCs would no longer matter for functionality to be consistent with the way OOB applications us UCs.
  • The above is somewhat contrived as one of the biggest use cases is where you’d already HAVE a glide record to work with as part of the platform (BRs & ACLs mainly).

Usage when the UCs are associated via a related list is a bit more complicated….but just because data model is a more complicated. The overall approach is the same in that we want to build an array of UCs for the can read and cannot read related lists and feed them to base function. 

var gr = new GlideRecord("my_custom_table");
gr.get("");
var user_can_read = new UserCriteria(gs.getUserID(), gr).evalUCRelatedList("m2mCanTable", "canGrRefFieldName", "canUCRefFieldName", "m2mCannotTable", "cannotGrRefFieldName", "cannotUCRefFieldName");

//m2mCanTable: name of the table that contains the list of can read UCs related to the record in my_custom_table.
//canGrRefFieldName: reference to a record on my_custom_table.
//canUCRefFieldName: reference to a UC record
//m2mCannotTable: name of the table that contains the list of cannot read UCs related to the record in my_custom_table.
//cannotGrRefFieldName: reference to a record on my_custom_table
//cannotUCRefFieldName: reference to a UC record

Hopefully, this is helpful and gives the ServiceNow community an extra tool in their toolbox to solve their problems with the power of the ServiceNow platform!

NOTE:

  • UserCriteriaLoader is NOT documented on docs.servicenow.com or in developer.servicenow.com. However, if you search in OOB script includes you will see it being used. So a bit of ‘buyer beware’ but we believe it’s relatively safe to use….i.e. while not formally documented it seems unlikely that ServiceNow would deprecate the object/functions.
  • The script include given was factored for the use case/need we had at the time, it can certainly be reworked to fit your needs…but the building blocks are there for most use cases.

var UserCriteria = Class.create();
UserCriteria.prototype = {
    /**
    name: initialize
    description: Initializes the UserCriteria object by setting the user sys_id and the GlideRecord.
    param: {String} [userSysId] - sys_id of the user we want to check.
    param: {GlideRecord} [grToCheck] - GlideRecord of the record we want to check against.
    example:
    var matches = new UserCriteria(userSysID, grToCheck)
    returns: {UserCriteria} returns a initialized UserCriteria Object.
    */
    initialize: function(userSysId, grToCheck) {
        this.userSysId = userSysId;
        this.grToCheck = grToCheck;
    },

    /**
    name: evalUCGlideList
    description: Takes the field names on the table/GlideRecord that are GlideList(s) pointing too User Criteria for "Can" & "Cannot".
    param: {String} [ucCanField] - The field name of the GlideList for the "Can" User Criteria.
    param: {String} [ucCannotField] - The field name of the GlideList for the "Cannot" User Criteria.
    example:
    var matches = new UserCriteria(userSysID, grToCheck).evalUCGlideList("<can_read_uc_glidelist_field>", "<cannot_read_uc_glidelist_field>");
    returns: {boolean} Returns true if user passed in is part of any of the user criteria in the GlideList. 
    */
    evalUCGlideList: function(ucCanField, ucCannotField) {
        var ret = false;
        if (ucCannotField)
            ret = (!this._matchesUCListField(ucCannotField, false) && this._matchesUCListField(ucCanField, true));
        else
            ret = (this._matchesUCListField(ucCanField, true));

        return ret;

    },

    /**
    name: evalUCRelatedList
    description: Takes m2m tables for can and can't mappings, with ref field names of "connectors", that connects a record to User Criteria and returns true/false indicating a users access depending on related lists.
    param: {String} [m2mCanTable] - Name of the m2m table connecting the record with User Criteria 'can' access.
    param: {String} [canGrRefFieldName] - Name of the field on the m2m table that points to the GlideRecord (can access).
    param: {String} [canUCRefFieldName] - Name of the field on the m2m table that points to the User Criteria (can access).
    param: {String} [m2mCannotTable] - Name of the m2m table connecting the record with User Criteria for can't access
    param: {String} [cannotGrRefFieldName] - Name of the field on the m2m table that points to the GlideRecord (can't access).
    param: {String} [cannotUCRefFieldName] - Name of the field on the m2m table that points to the User Criteria (can't access).
    example:
    var matches = new UserCriteria(userSysID, grToCheck).evalUCRelatedList(m2mCanTable, canGrRefFieldName, canUCRefFieldName, m2mCannotTable, cannotGrRefFieldName, cannotUCRefFieldName)
    returns: {boolean} Returns true if user passed in is part of any of the user criteria in the GlideList. 
    */
    evalUCRelatedList: function(m2mCanTable, canGrRefFieldName, canUCRefFieldName, m2mCannotTable, cannotGrRefFieldName, cannotUCRefFieldName) {

        var ret = false;
        if (m2mCannotTable)
            ret = (!this._matchesUCListTable(m2mCannotTable, cannotGrRefFieldName, cannotUCRefFieldName, false) && this._matchesUCListTable(m2mCanTable, canGrRefFieldName, canUCRefFieldName, true));
        else
            ret = this._matchesUCListTable(m2mCanTable, canGrRefFieldName, canUCRefFieldName, true);
        return ret;

    },

    /**
    name: _matchesUCListField
    description: Takes the field name on the table/GlideRecord that is a GlideList pointing too User Criteria.
    param: {String} [ucField] - The field name of the GlideList for the User Criteria.
    param: {boolean} [emptyListReturn] - What to return in case the list of criteria is empty (false: for cannot read; true: for can read).

    returns: {boolean} Returns true if user passed in is part of any of the user criteria in the GlideList. 
    */
    _matchesUCListField: function(ucField, emptyListReturn) {

        var ucArray = [];
        var ucFieldValue = this.grToCheck.getValue(ucField);
        if (ucFieldValue != null)
            ucArray = ucFieldValue.split(",");

        return (ucFieldValue && !gs.isLoggedIn()) ? !emptyListReturn : this._matchesUCList(ucArray, emptyListReturn);

    },

    /**
    name: _matchesUCListTable
    description: Takes a m2m table, with ref field names of "connectors", that connects a record to User Criteria and returns true/false indicating if the user is in any of the User Criteria.
    param: {String} [m2mTable] - Name of the m2m table connecting the record with User Criteria
    param: {String} [grRefFieldName] - Name of the field on the m2m table that points to the GlideRecord.
    param: {String} [ucRefFieldName] - Name of the field on the m2m table that points to the User Criteria.
    param: {boolean} [emptyListReturn] - What to return in case the list of criteria is empty.
    
    returns: {boolean} Returns true if user passed in is part of any of the user criteria in the GlideList. 
    */
    _matchesUCListTable: function(m2mTable, grRefFieldName, ucRefFieldName, emptyListReturn) {

        //var ret = false;
        var ucArray = this._glideQuerytoArray(m2mTable, grRefFieldName, ucRefFieldName);
        return (ucArray.length > 0 && !gs.isLoggedIn()) ? !emptyListReturn : this._matchesUCList(ucArray, emptyListReturn);

    },


    /**
    name: individualUserCriteriaCheck
    description: Used to check the includance of a user in a single User Criteria. 
    param: {String} [uc] - Sys_id of a single User Criteria
    example:
    var matches = new UserCriteria(userSysID, grToCheck).individualUserCriteriaCheck("<uc_sys_id>")
    returns: {boolean} Returns true if user passed in is part of any of the user criteria. 
    */
    individualUserCriteriaCheck: function(uc) {

        var ucArrayofOne = [uc];
        return this._matchesUCList(ucArrayofOne, false);

    },

    /**
    name: _matchesUCList
    description: Makes call to OOB UserCriteriaLoader for evaluation of user/criteria and takes in to account empty user criteria.
    param: {Array} [ucArray] - Array of relevent User Criteria sys_ids.
    param: {boolean} [emptyListReturn] - what to return if User Criteria array is empty.

    returns: {boolean} boolean if user is in User Criteria passed.
    */
    _matchesUCList: function(ucArray, emptyListReturn) {

        var ret = false;
        if (this.isEmpty(ucArray))
            ret = emptyListReturn;
        else
            ret = sn_uc.UserCriteriaLoader.userMatches(this.userSysId, ucArray);
        return ret;

    },

    /**
    name: isEmpty
    description: Checks if the inpur array is empty or contains only inactive records.
    param: {Array} [ucArray] - Array of relevent User Criteria sys_ids.
    example:
    this.isEmpty(ucArray)
    returns: {boolean} True if array has no elements or if all associated records are inactive.
    */
    isEmpty: function(ucArray) {
        if (ucArray.length == 0) {
            return true;
        }
        var ucGr = new GlideRecord("user_criteria");
        ucGr.addActiveQuery();
        ucGr.addQuery("sys_id", "IN", ucArray);
        ucGr.setLimit(1);
        ucGr.query();
        return !ucGr.hasNext();
    },

    /**
    name: getAllUserCriteria
    description: Given a user sys_id returns the list of User Criteria that user is part of.
    example:
    new UserCriteria(userSysID, grToCheck).getAllUserCriteria()
    returns: {String} - List of User Criteria sys_id's that the user is part of.
    */
    getAllUserCriteria: function() {

        return sn_uc.UserCriteriaLoader.getAllUserCriteria(this.userSysId);

    },

    /**
    name: _glideQuerytoArray
    description: An internal/hidden function used to look up the related User Criteria records and build them in to an array.
    param: {String} [m2mTable] - Name of the m2m table connecting the record with User Criteria
    param: {String} [grRefFieldName] - Name of the field on the m2m table that points to the GlideRecord.
    param: {String} [ucRefFieldName] - Name of the field on the m2m table that points to the User Criteria.
    
    returns: {Array} An array of User Criteria that are related to the record via the m2m table.
    */
    _glideQuerytoArray: function(m2mTable, grRefFieldName, ucRefFieldName) {

        var ucArray = [];
        var grUC = new GlideRecord(m2mTable);
        grUC.addQuery(grRefFieldName, this.grToCheck.sys_id);
        grUC.query();
        while (grUC.next()) {
            ucArray.push(grUC.getValue(ucRefFieldName).toString());
        }

        return ucArray;
    },

    type: 'UserCriteria'
};

 

The post Leveraging User Criteria in your custom applications appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/scripting/leveraging-user-criteria-custom-applications/feed/ 6