Replacing Inaccessible APIs with Utility Script Includes in Scoped Applications - ben-vargas/servicenow-wiki GitHub Wiki

This article explains how to overcome limitations in scoped applications by creating reusable utility Script Includes. Specifically, it demonstrates how to replace the inaccessible gs.dateGenerate() method with a custom function that achieves the same result, while adhering to scoped application restrictions. This approach promotes reusable code and maintainability in your scoped applications.

The Challenge

Scoped applications in ServiceNow have restrictions on the APIs they can access. This can be an issue when a global scope API is needed. A common example of this is the gs.dateGenerate() method, which is not directly available within scoped applications. This method allows for easy creation of dates in a specific format. When building scoped applications, this limitation can require using alternative methods to achieve the same results.

The Solution

The solution is to create a utility Script Include that contains a replacement for the inaccessible method. This approach offers:

  • Reusability: Utility Script Includes can be shared and used across multiple scripts within the scoped application and by other scoped applications if configured appropriately.
  • Maintainability: Centralizing code in a Script Include promotes easier updates and maintenance.
  • Scope Compliance: The utility code will work within the context of a scoped application, adhering to scope restrictions.
  • Flexibility: By creating your own custom logic, you can tailor the utility to your specific needs.

Creating a Utility Script Include:

  1. Create a New Script Include:

    • Navigate to System Definition > Script Includes.
    • Create a new Script Include with the following values:
      • Name: Tools
      • Client callable: Checked (optional but useful for some scenarios)
      • Accessible from: All application scopes (if you intend to share this with other apps)
      • Script: Replace the default code with the code below.
  2. Script Include Code:

     var Tools = {};
    
     Tools.dateGenerate = function(date, range) {
         if (!range)
             range = '12:00:00';
    
         var gdt = new GlideDateTime();
         if (range.equals("start"))
             gdt.setDisplayValueInternal(date + " 00:00:00");
         else if (range.equals("end"))
             gdt.setDisplayValueInternal(date + " 23:59:59");
         else
             gdt.setDisplayValueInternal(date + " " + range);
    
         return gdt;
     };
    
    
     Tools.fixFilter = function(filterString) {
         if (!filterString)
             return filterString;
    
         //lets ensure we are working on a string:
         filterString = filterString + '';
    
         //fix dateGenerate
         var dateGenerateRegex = /gs\.dateGenerate/gm;
         var ourDateFunction = "Tools.dateGenerate";
    
         return filterString.replace(dateGenerateRegex, ourDateFunction);
     };
    

Explanation:

  • var Tools = {};: This defines an empty object. This is a best practice for utility classes as it helps to prevent unnecessary creation of new class instances.
  • Tools.dateGenerate = function(date, range) { ... }: This creates a custom dateGenerate function, designed to behave like the global gs.dateGenerate.
    • It takes a date and range parameter. The range parameter defaults to 12:00:00 if it's missing.
    • It creates a GlideDateTime object and uses setDisplayValueInternal to parse the date string and return it.
  • Tools.fixFilter = function(filterString) { ... }: This creates a custom fixFilter function that will replace instances of the gs.dateGenerate function with the custom Tools.dateGenerate function.
    • This will take a filter string and replace all instances of gs.dateGenerate with Tools.dateGenerate.

Using the Utility Script Include

  1. Access from within Scoped Applications

    • Within the scoped application, you can call the functions on the Tools object directly, using Tools.dateGenerate and Tools.fixFilter methods.
  2. Using with Encoded Queries:

    • Encoded queries frequently use the gs.dateGenerate method.
    • To use your custom Tools.dateGenerate in an encoded query, you can use the Tools.fixFilter method to replace any instances of gs.dateGenerate with the custom method.
    • Example:
        var enq = "opened_atBETWEENjavascript:gs.dateGenerate('2024-01-01','12:00:00')@javascript:gs.dateGenerate('2024-12-31','12:00:00')";
        var inc = new GlideRecord('incident');
        inc.addEncodedQuery(Tools.fixFilter(enq));
        inc.query();
        gs.info("Incidents matching query: " + inc.getRowCount());
    
    
  3. Access from other scoped applications

    • If the Script Include is configured to be accessible to other application scopes, other scoped applications can take advantage of the utility, for example:
          var enq = "opened_atBETWEENjavascript:gs.dateGenerate('2024-01-01','12:00:00')@javascript:gs.dateGenerate('2024-12-31','12:00:00')";
          var inc = new GlideRecord('incident');
          var Tools = sn_custom_app.Tools; //Replace sn_custom_app with the scope that this script include exists in.
      
          inc.addEncodedQuery(Tools.fixFilter(enq));
          inc.query();
      
          gs.info("Incidents matching query: " + inc.getRowCount());
      

Best Practices

  • Utility Script Includes: Develop utility Script Includes to centralize commonly used functions.
  • Naming Conventions: Use descriptive names for your Script Includes and their methods.
  • Scope Control: Only expose your utility includes to the application scopes that need them, to prevent unintended access.
  • Comments: Add comments to your code to explain the purpose and logic.
  • Testing: Always test utility classes thoroughly in a development environment before deploying to production.
  • Error Handling: Consider adding error handling to your utility methods to catch unexpected input or data issues.
  • Modularity: Use a modular design pattern, keeping each utility focused and limited to a specific task. This will improve maintainability and allow other developers to quickly understand the core purpose of the method.
  • Accessibility: When making your utility classes accessible to other scopes, make sure that the public API is stable and you will not be changing the core function without notifying your users.

Why This Approach is Valuable

  • Overcomes Scope Limitations: It provides an alternative for inaccessible APIs within scoped applications.
  • Promotes Reusability: The custom code can be easily reused by any other scripts in the application.
  • Improves Maintainability: Encapsulates logic, making your application easier to update and maintain.
  • Encapsulation: Protects the internal state of your scripts, preventing accidental overwrites.

Conclusion

This article demonstrates how to use a utility Script Include to overcome the restrictions of scoped applications by creating a replacement for the global gs.dateGenerate() method. By adopting this approach, you can build robust and flexible scoped applications while adhering to platform constraints. Remember to test thoroughly in a non-production instance before deploying to production.