This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Architecture and General Notes

ServiceNow architecture considerations and other considerations.

Introduction

I’ve worked with the ServiceNow system for about 3 years now. ServiceNow is a combination of a SaaS and IaaS sytem that provides out-of-box functionality for ITIL and general service management functionality.

I’ve worked on development frameworks in older systems of the past. The Glide framework underneath the ServiceNow platform is truly revolutionary for bringing software development into the 21st century. It is well thought out where the developer can concentrate mostly on process rather than re-invent rudimentary code to support application aspects such as navigation and UI form presentation. This is all supplied as part of the core framework. The customer/developer facing elements are stored as configuration elements, including data definition. The customer/developer has no direct access to the backend database. All data definition is stored as meta data using the same semantics into the database as any application developed on top of the platform itself.

Database records are displayed in one of two formats: “list view” where a number of records are viewed in a column/row format and “UI view” where fields on a single record is displayed and possibly a list of related records displayed in the parent record’s view.

Other backend features include a job scheduler, mail server, event handler, integrations using SOAP, REST, incoming email parser, and “runbook”.

1 - To "Service Catalog" or Not

The Service Catalog operates as a place where an end user can order something, whether it is physical or a request for service. In its basic form it is presented much like a product and handled as when you buy som e product off of Amazon. Behind the “product” is a workflow attached to process its fulfillment. Service C atalog is a great tool, but not the answer to all situations. This article talks about what it does well an d where it does not fit.

Introduction

In a nutshell, the Service Catalog is a framework that was originally built to simplify creating and presenting products (whether a service or a physical product) from a catalog and attach an execution workflow behind the individual product. The framework mimics the e-commerce “shopping cart” experience such as Amazon. At a company that I used to work for, I was thrown into the aftermath of the initial installation projects that included the Service Catalog to organize and clean up the hurried installation as well as standardize and manage the growth over the ServiceNow service management solution. I saw first hand how not to implement the Service Catalog. So when should you leverage the Service Catalog or roll your own by extending the Task table?

The Service Catalog Framework

The basic process flow of the Service Catalog is to select one or more items the client desires that are added to a “cart”. When the order is submitted, approvals are processed over the entire cart aka the “request”. After this initial approval, the “requested items” begin their respective workflows that handles the particulars for fulfillment of each item ordered. (SN has enhanced functionality a bit to include a “one step” submit process. The same overall workflow is still performed, the customer just doesn’t see the cart.)

This framework works well for physical products selectable from a catalog and placed in a shopping cart. Where you have an intangible service offering, it isn’t so straight forward. At the heart of a service request, it is a task that has a workflow managing its fulfillment. Approvals may or may not be needed. A cart is probably not needed since the customer is only interested in a single service request to be performed. From a developmental standpoint, the question here is whether you extend the Task table for another application or do you create another service catalog item to collect and process a service request?

Some other features to consider from the Service Catalog perspective is whether a service request is intended to be part of a broader process such as new employee provisioning. Reporting is another consideration. In a more recent version, variable reporting was introduced. As of the Fuji release, reporting on variables required open and broad read access to the variables related tables. There is no granularity over read access for reporting. Reporting users would have to be all enabled or not. Depending on the privacy of the information stored as a variable, you may not want to enable variable reporting.

So Do I Roll My Own or Use the Service Catalog?

From practical experience, reporting has traditionally driven whether I extend the Task table or simply configure another service catalog item. I was not comfortable to open access on the variables table so that the basic “itil” roled user could report on variables.

Below are some lessons I learned from contending with a cleanup over the Service Catalog.

  • Do not modify the Service Catalog framework itself (namely the business rules) under the Request and Requested Items tables. REPEAT: DO NOT MODIFY THE SERVICE CATALOG FRAMEWORK ITSELF. Use it as it comes. Service Catalog has matured and seems to continue to evolve. Making mods to the SC framework will make it difficult to upgrade and enable new features as the baseline software is enhanced over time.
  • Standardize approval workflows. Consider making “template” workflows to get you started on a new workflow, or experiment with subflows to normalize approval workflows.
  • Maintain a separate product catalog appropriately for the type of product being offered (e.g. desktop workstations versus software packages versus service offerings). You can create your own product catalog that will help to better organize presentation of catalog items by category or type.
  • Avoid “streamlining” by creating a single multi-value variable over a table in which the individual items each require its own fulfillment (e.g. the Configuration Item table for multiple software selection). Approvals have to apply to all items selected and not related to individual items selected. From the workflow automation perspective, it becomes more difficult to manage individual approvals related to each item selected. Take a look here for some information over approval engines.
  • If the requirements of the Service Catalog Item doesn’t fit the basic shopping cart experience (i.e. Request with one or more Request Items), each with it’s own workflow process, consider extending the “task table” specifically for that purpose.

One regret I had for using the Service Catalog as the solution was a scenario where an intermediate group took a Requested Item and further defined detail requirements before passing on the request to one of a number of fulfillers depending on another (related) Catalog Item, each requiring a different workflow for fulfillment. I was successful at being able to create another Request/Requested Item and relate the two Requested Items together into one Request out of the primary workflow of the first Requested Item, but was a bit of a mess in managing the two Requested Items in tandem due to one being subordinate to the other though peers. If I had it to do over again, I would have extended the “task table” and rolled my own, possibly creating an independent Request/Requested item for fulfillment and maintain a related list to the custom task table extension.

Other Thoughts

One subject that needs mentioned is the Catalog Task. As of Fuji, Catalog Tasks are a generic task that is subordinate to the Requested Item and initiated and maintained out of the workflow for the Requested Item. There is only a single workflow (or set of workflows with conditions for execution) that can be defined generically for all Catalog Tasks as any other table (except the Catalog Item table) with no regard to any one Catalog/Requested Item. This limits how you develop around the Catalog Task to better manage fulfillment tasks between departments. I would like to see a future enhancement where a configuration table similar to the Catalog Item table that associates a named workflow that is automatically initiated that would provide a specific workflow under a Catalog Task as it would pertain to the related Request Item. As it exists today, you’d have to add a custom attribute to the Catalog Task table for a “category” and condition all workflows based on that category for execution. Since the Catalog Task is generic, more than a few conditional workflows would affect performance as well as become convoluted since its maintenance is over all Catalog Tasks.

2 - Creating and Using Subflows in ServiceNow

Subflows are separate workflow activities defined to be executed out of a primary workflow. ServiceNow’s documentation gives the basics on how to create the subflow, but lacks on how to link a subflow to a primary workflow and pass data and return codes back and forth. This article attempts to fill in the gaps you can’t get from SN documentation.

The basics for subflows is documented in this ServiceNow Wiki article.

Notes to bear in mind when defining and using subflows:

  • Create the subflow prefaced with “Subflow” in order to distinguish that subflow from a regular workflow. Otherwise you can’t tell in the list whether the workflow is a primary or sub.
  • Subflows are available as workflow activities when creating a primary workflow.
  • Subflows must be created using the same table as the primary workflow.
  • Input fields:
    • The fields must be mapped into the record for the table the subflow is created. Must exist in the same context.
    • If a literal, no quotes needed.
  • The workflow scratchpad in the primary workflow is not shared with the subflow and vice versa.
    • Data is received as “input fields”.
    • Data is returned using “return codes”.
    • If scratchpad variable is used for receiving a “return code”, it must be initialized before being referenced in the Return Code activity.
  • Return codes:
    • Can return a string (or in the form of a Javascript $() style variable).
    • Literals do not need quoting.
  • Change considerations:
    • Subflows are dynamically executed. Tasks already in process will execute the current subflow when called unless the subflow is already in execution mode (i.e. already attached to the task).
    • Workflows are statically executed. Tasks already in process will execute the legacy workflow since it is already in progress.

Example: A simple catalog item is created with a workflow associated that calls a subflow to return whether a manager’s name begins with the letter “A”. The return from the subflow would be either “yes” or “no”. This functionality would be referenced in this catalog item and others as a standard validation on other catalog items that have their own unique workflow, but requiring the same “A” validation on the manager’s name.

The system property “glide.workflow.enable_input_variables” must be set to true in order to enable input variables in subflows.

3 - Working with Dates in ServiceNow

One would think that the date/time data type would be cut and dry in terms of computing. After all, programming around date/time has been a feature since the early days of digital computer systems. ServiceNow has to provide backend utilities to perform this translation since the developer cannot reach into the web server backend. Though SN has a collection of date/time utilities, they are scant on formatting and calculation options. You have to roll your own to compensate using the Glide library functions that SN makes available to the developer for translating a system or database stored time into a constant, generic format. This article provides an example for how to create your own date/time utility utilizing the Glide library date/time functions.

Introduction

Different operating systems handles date/time using different standards. Databases stores date/time using even differing standards than the OS. Aside from storing and interpreting a constant date/time, formatting the presentation can be onerous. Remember the Y2K dilemma? In terms of open systems, date/time has been a non-event all the way around. The value stored is the number of seconds past “epoch” (i.e. January 1, 1970) in Universal Time Code (i.e. Greenwich Meantime) and converted depending on the timezone specified in the computing environment the host is running. Since this is expressed in integer form, time runs out, so to speak in January 2038. Databases have their own epoch/integer system for storing date/time. A translator to interpret a date/time is needed to present a date/time in various formats.

Use Case

On one application that I had to build, only the date itself was needed. Defining the data dictionary was no big deal since the field was definable as a “date only”. In calculating date differences in a Business Rule, I had to role my own utilities for calculating and storing differences between two dates since the Glide calls only serviced date/time. Below are some basic Javascript functional objects for working with date/time and passing the result to the client via Ajax. Some are mine, some are from various sources made available in the SN community that I have collected. The functionality is self documenting.

var AjaxUtils = Class.create();

AjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

        calcDateDiff : function(start_date, end_date) {
		// This method calculates on whole days.
		// The "start day" counts as 1 day.
		
		var createdate = new GlideDateTime();
		var enddate = new GlideDateTime();
		
		// The current day is used for the endDate calculation if not provided.
		if (typeof(start_date) == 'undefined') {
			if (this.getParameter('sysparm_start_date')) {
				start_date = this.getParameter('sysparm_start_date');
			} else {
				start_date = gs.now();
			}
		}
		if (typeof(end_date) == 'undefined') {
			if (this.getParameter('sysparm_end_date')) {
				end_date = this.getParameter('sysparm_end_date');
			} else {
				end_date = gs.now();
			}
		}
		
		createdate.setDisplayValueInternal(start_date + ' 00:00:00');
		enddate.setDisplayValueInternal(end_date + ' 00:00:00');
		
		var variance = parseInt(gs.dateDiff(createdate.getDisplayValue(), enddate.getDisplayValue()).replace(/ .*/, '') );
		variance += 1;
		
		
		return variance.toString();
		
	},
	
	checkEndDate : function(startDate, endDate) {
		if(typeof(startDate) == 'undefined') {
			//startDate = new Packages.com.glide.glideobject.GlideDateTime();
			//startDate.setDisplayValue(this.getParameter('sysparm_start_date'));
			startDate = this.getParameter('sysparm_start_date');
		}
		if(typeof(endDate) == 'undefined') {
			//endDate = new Packages.com.glide.glideobject.GlideDateTime();
			//endDate.setDisplayValue(this.getParameter('sysparm_end_date'));
			endDate = this.getParameter('sysparm_end_date');
		}
		//gs.log('here:\n' + startDate + '\n' + endDate + '\n' + gs.dateDiff(startDate, endDate, true));
		return(gs.dateDiff(startDate, endDate, true) >= 0);
	},
	
	returnLeadTimeInDays : function(startDate) {
		var startDateTmp = new GlideDate();
		startDateTmp.setDisplayValue(startDate);
		
		var msStart = startDateTmp.getNumericValue();
		var msNow = new GlideDate().getNumericValue();
		var msDiff = msStart - msNow;
		var dayDiff = Math.floor(msDiff / 86400000);
		
		return(dayDiff);
	},
	
	
	checkLeadTime : function(startDate, leadTime) {
		startDate = new GlideDateTime();
		startDate.setDisplayValue(this.getParameter('sysparm_start_date'));
		leadTime = parseInt(this.getParameter('sysparm_lead_time'));
		
		var msStart = startDate.getNumericValue();
		var msNow = new GlideDateTime().getNumericValue();
		var msDiff = msStart - msNow;
		var dayDiff = Math.floor(msDiff / 86400000);
		
		return(dayDiff >= leadTime);
	},
	
	dateInFuture : function() {
		//Returns true if date is before now, and false if it is after now.
		var firstDT = this.getParameter('sysparm_fdt'); //Date-Time Field
		var diff = gs.dateDiff(firstDT, gs.nowDateTime(), true);
		var answer = '';
		new_diff = Math.floor(diff / 86400);
		if (new_diff >= 0){
			answer = 'true';
		} else {
			answer = 'false';
		}
		return answer;
	},
	
	dateInPast : function(firstDT) {
		//Returns true if date is before today, and false if it is today or later.
		if (typeof(firstDT) == 'undefined') {
			firstDT = this.getParameter('sysparm_fdt'); //Date-Time Field
		}
		var diff = gs.dateDiff(firstDT, gs.nowDateTime(), true);
		var answer = '';
		new_diff = Math.floor(diff / 86400);
		if (new_diff > 0){
			answer = 'true';
		} else {
			answer = 'false';
		}
		return answer;
	},
	
	date_timeInPast : function(firstDT) {
		//Returns true if date is before today, and false if it is today or later.
		if (typeof(firstDT) == 'undefined') {
			firstDT = this.getParameter('sysparm_fdt'); //Date-Time Field
		}
		var diff = gs.dateDiff(firstDT, gs.nowDateTime(), true);
		var answer = '';
		new_diff = Math.floor(diff / 60);
		if (new_diff > 0){
			answer = 'true';
		} else {
			answer = 'false';
		}
		return answer;
	},  type: "AjaxUtils"
});