Implementing a Factory Pattern on ServiceNow
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:
- Create the code that would call each company’s API correctly.
- Create a blank/dummy script include (this will be our factory).
- Get GlideRecord reference to our factory.
- Overwrite the script value from 3 with the string that represents one of our script includes from step 1.
- 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
grFactory.script = "CompanyOneAPI";
- 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();
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;
};
- 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:
- Create CompanyThreeAPI (with an execute method) that calls the other systems API to create/update incidents.
- 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!
Date Posted:
December 5, 2024
Share This:
Related Posts
Fresh Content
Direct to Your Inbox
Just add your email and hit subscribe to stay informed.






