Skip navigation

Future Proofing Script Includes

score 0
You have not voted. Active

The approach we’ve documented makes adding new script includes for making table modifications take minutes to implement and most importantly the code does not need to be changed if attributes are added or removed from a table.

Most of the coding needed to implement new features in ServiceNow consists of manipulating table data. One of the most significant, and difficult to manage, features of ServiceNow is the ease by which tables can be extended and modified to introduce new data or behaviors.

 

The approach we've developed allows us to put all the code needed to implement CRUD changes in a single place for all tables and makes implementing these features for any new tables something that can be done in seconds. Best of all, you never (literally NEVER) need to revisit the code if new fields are added to the table.

 

For the ‘tldr’ audience I’m going to show the approach we’ve implemented and those who want more details on the ‘why’ can read the attached document. All the scripts below are script include definitions.

 

Add the following file to your script includes:

u_BASE_Table

var u_BASE_Table = Class.create();

u_BASE_Table.prototype = {

    initialize: function() {

              // gs.log('u_BASE_Table class instantiated');

              this.tableName = null; // will be defined in parent class definition

    },

      

       updateRecord : function(input) {

              var str = '';  // Temporary string for message construction.

             

              // gs.log('updateRecord(input) = ' + JSUtil.describeObject(input));

             

              // Define the return object

              var result = {};

              result.success = false;

              result.returnCode = null;

              result.message = null;

              result.data = null;

             

              // Parse the input.

              var request = JSON.parse(input);

              var grRec = new GlideRecord(this.tableName);

              grRec.initialize();

             

              if (typeof request.sys_id != 'undefined') {

                     // This is an update!!!

                     grRec.get(request.sys_id);

              }

             

              // Create the record.

              for (var attribute in request) {

                     // Exclude attempting to update the sys_id.

                     if (attribute != 'sys_id') {

                           // gs.log('updateRecord(): Checking validity of field ' + attribute);

                          

                           if (grRec.isValidField(attribute)) {

                                  grRec[attribute] = request[attribute];

                           }

                           else {

                                  // Regardless of update success/fail - list the invalid fields on return.

                                  str = 'Field not valid ' + attribute;

                                  result.message = ((result.message == null) ? (result.message = str) : (result.message += '; ' + str));

                           }

                     }

              }

             

              try {

                     var returnCode = grRec.update();

                     result.success = true;

                     result.returnCode = returnCode.toString();

                     str = this.tableName + ' record updated.  sys_id: ' + returnCode;

                     result.message = ((result.message == null) ? (result.message = str) : (result.message += '; ' + str));

                     // gs.log('updateRecord(): ' + str);

                     result.data = result.returnCode;

              }

              catch (ex) {

                     str = this.tableName + ' record not updated.';

                     result.message = ((result.message == null) ? (result.message = str) : (result.message += '; ' + str));

                     // gs.log('updateRecord(): ' + str);

                     result.data = null;

              }

                    

              return JSON.stringify(result);

       },

      

       getRecord : function(input) {

             

              //gs.log('getRecord(input) = ' + JSUtil.describeObject(input));

             

              // Define the return object.

              var result = {};

              result.success = false;

              result.returnCode = null;

              result.message = null;

              result.data = null;

             

              // Parse the input.

              var request = JSON.parse(input);

             

              //gs.log('getRecord parsed input = ' + request);

             

              // Retrieve the record.

              var record = {};

              var grRec = new GlideRecord(this.tableName);

              grRec.initialize();

              if (grRec.get(request.sys_id)) {

                     // gs.log('getRecord(): Getting data for ' + this.tableName + ' ' + grRec.number);

                     for (var attribute in grRec) {

                           // Declare value holders to keep them in scope.

                           var value = '';

                           var displayValue = '';

                          

                           value = grRec[attribute].toString();

                           try {

                                  displayValue = grRec[attribute].getDisplayValue();

                           }

                           catch (ex) {

                                  // Do nothing.

                           }

                          

                           record[attribute] = value;

                           if (value != displayValue) {

                                  record[attribute + '_dv'] = displayValue;

                           }

                     }

                    

                     result.success = true;

                     result.message = this.tableName + ' details returned in data obj';

                     result.data = record;

              }

              else {

                     result.message = this.tableName + ' record could not be retrieved';

              }

             

              var resultJSON = JSON.stringify(result);

              // gs.log('getRecord(): result = ' + JSUtil.describeObject(resultJSON));

             

              return resultJSON;

       },

      

       newRecord : function(input) {

              // Since GlideRecord update() will perform an insert if the record is not present

              // we just pass the input as is (JSON) to the updateRecord function and pass

              // back the return object (JSON) as is with no additional processing.

              return this.updateRecord(input);

       },

 

    type: 'u_BASE_Table'

};

 

Now, for each table you want to implement CRUD operations against simply create a new script include and make a small edit to enable calls to the base table. The example below is for the sys_user table. Disclaimer – we don’t use the ‘new’ function on sys_user since we import from LDAP, this is just an example applicable to any table.

 

var u_USER = Class.create();

u_USER.prototype = Object.extendsObject(u_BASE_Table, {

    initialize: function() {

              this.tableName = 'sys_user';

    },

      

        newUser: function(input) {

              return new u_USER().updateRecord(input);

    },

   

    updateUser: function(input) {

              return new u_USER().updateRecord(input);

    },

   

    getUser: function(input) {

             

              return new u_USER().getRecord(input);

    },

      

       deleteUser: function(input) {

                    

              // nothing actually gets deleted, just set to inactive

              // assuming input already has the sys_id of the record to

              // inactivate so just need to add the 'active' attribute and

              // call an update

              input.active = false;

              return new u_USER().updateRecord(input);

    },

 

       type: 'u_USER'

});

 

The edit you’ll need to make after creating a new script include for a table which enables the call to the base table without having to explicitly reference it is to change the default structure of:

 

var <SI Name> = Class.create();

<SI Name>.prototype = {

    initialize: function() {

    },

   

    yourFunction : function() {

       // your code

    }

 

    type: '<SI Name>'

};

 

To (note we use a ‘friendly’ name in each function so the calling script can seem natural):

 

var <SI Name> = Class.create();

<SI Name>.prototype = {

       <SI Name>.prototype = Object.extendsObject(u_BASE_Table, {

    initialize: function() {

       this.tableName = 'sys_user'; // update this with table to maintain

    },

   

        newUser: function(input) {

              return new <SI Name>().updateRecord(input);

    },

   

    updateUser: function(input) {

              return new <SI Name>().updateRecord(input);

    },

   

    getUser: function(input) {

             

              return new <SI Name>().getRecord(input);

    },

      

       deleteUser: function(input) {

                    

              input.active = false;

              return new <SI Name>().updateRecord(input);

    },

 

    type: '<SI Name>'

}); // don’t forget this closing paren

 

These three minor edits I’ve highlighted along with the functions defined enables all functions you’ll ever need to maintain any table in ServiceNow and you’ll never have to change the code when the table attributes change. Of course, we usually add additional functions for calculations, etc. but we always use these defined functions for actual table changes or retrievals. Makes everything behave the same.

Calling your script include from any script is as simple as the following example:

 

var input = {}; //create an object of attributes to modify - sys_id needed to find record

  1. input.sys_id = '6526372b4f22c20078fd97dd0210c765';
  2. input.cost_center = '3312';
  3. input.employee_number = '98765432';

var inputObj = JSON.stringify(input); //convert to JSON

 

// Call the server side script include to update the record.

var resultObj = new u_USER().updateUser(inputObj);

 

// Process the result.

var result = JSON.parse(resultObj); // convert from JSON

 

  1. gs.log('success = ' + result.success);
  2. gs.log('returnCode = ' + result.returnCode);
  3. gs.log('message = ' + result.message);
  4. gs.log('data = ' + result.data);

 

Notice we always pass and retrieve JSON with our script includes. We do a lot of REST calls and default to JSON for passing data back and forth. This lets us use the REST data natively but it’s simple to convert to/from JSON with javascript objects.

  The ‘get’ function is enhanced to retrieve the display value for any reference field so you get both the reference sys_id and the display in two fields. An example might be ‘user’ returning the sys_id reference to the sys_user table and ‘Penelope Pitstop’ in the field ‘user_dv’.

Comments

Vote history