An example integration of Agility Platform and ServiceNow

Female coder CSC Blogs

Since I recently blogged about different integration options for Agility Platfrom and ServiceNow here, I figured a good follow up would be to give an example of how Agility could open Change Requests or Incidents automatically in ServiceNow.

The idea behind this is that when a Project/Blueprint/Instance is spun up by Agility, the customer might have a requirement to log information into ServiceNow for additional post processing. For instance, the customer might have some external processes that must be followed when a new instance is spun up. To kick off that process, a change request is made within ServiceNow with basic details. My immediate thought is that Agility could perform many of those actions via the LifecyclePostProcess script. But for this example customer, it’s important to only open the Change Request at this time.

A few weeks after I created the initial example, I went back and cleaned the script up a little bit and add an option to do either a Change Request or an Incident. Other options could be added, such as adding new Configuration Items (CIs) into the CMDB/CMS in ServiceNow (or some other third party system).

 Disclaimers

This script is provided as an example only and is not considered part of the product. This script could be written several ways and should not be considered the one and only way to write them, best practices, etc… it is an example.  Also, 99% of the script is java/javascript and has nothing to do with Agility or the Agility API. There are a few sample Agility API calls as reference point. As always, only play with these types of things on a development system that closely matches production. And finally…

  • Some assembly required
  • Batteries not included
  • Your results may vary

Tips

From time to time, when I want to test a part of a script, specific syntax of a java call or maybe even an entire script, I do it within the Rhino shell provided by Mozilla from this link: Download Rhino – Mozilla | MDN.  This tool can only be used to test out javascript syntax and not full scripts used in Lifecycle Policies.

Keep in mind that since you are running outside of Agility (in Rhino), you will not be able to access pre-defined variables that would be made available to you within a Lifecycle policy script (as an example). You can however tests hundreds of java/javascript functions, such as opening/writing to a file, making Web services call to ServiceNow (or even Agility), inserting a row to a database, etc., etc., etc.

Getting Started

  • The script will need to be added as a LifecyclePostProcess Policy.
    • Go to Policy tab
    • Select the project on the left side
    • From drop-down, Create New, select Create New Policy
      • Give it a name and description
      • Policy Type: LifecyclePostProcess
      • API Version: I selected 3.2, but that really depends on any Agility related API calls you may add to this example.
      • For my testing, I selected Project with Start/Stop
      • Paste the script into the policies Script section. (MINOR EDITS OF SCRIPT REQUIRED, read further)
  • There are some basic settings that must be changed at the top of the script
    • var servicenow_url =   set this to the ServiceNow instance you are connecting to
    • var user_id =    set this to the user id that has permission to open change requests.  ie: admin
    • var password =    set this to the password for the above user_id
    • var serviceNowRequestType=   This controls the type of insert the script will do.   By default it is set to ‘change_request” and it will open a change request, the other option is ‘incident’.
    • var descriptionVal =    I have preset this to a hardcoded string.  You can comment out this line and use the other commented out one that uses generic Agility values such as the action performed (ie: start, stop) and the name of the item (IE: Project Name).
    • var fields_name_value =   This is an array of ServiceNow field names and values.  There is an example one for Change Requests and one for Incidents.  Only one should be uncommented and is based on serviceNowRequestType set previously.  When you open a change request (or incident) in Service Now there are a couple mandatory fields (may be different per customer implementation) and then optional fields.  You MUST provide all mandatory fields at a minimum otherwise a generic HTTP error is returned.   The pair is field name and field value.  So in the example script, the first Service Now Change Request field is “category” and it will be set to “Software”.  If you open a Service Now console, click Add, required fields have an asterisk next to them.
  • Releases of Agility Platform prior to 10.1, you will be required to add a few classPaths under Admin
    • java.net.URL
    • java.io.OutputStreamWriter
    • java.io.BufferedReader
    • java.io.InputStreamReader
    • javax.xml.bind
    • sun.net.www.protocol.https
    • sun.net.www.protocol.http
    • sun.net.www.http

No other changes should be required for the script.

Optional Script Overview:

For complete laziness, I typically create a log() function at the bottom of the script. Within that function I call the agility.logger.debug().   This is helpful to have a shorthand call to add to the agility log file.  I recommend using logging to troubleshoot your script, but please keep logging at a minimum for production.

In order to have a javascript make SOAP calls to another product, your javascript essentially needs to mimic a browser. The use of java.net.URL provides a mechanism to open a URL and then send and receive data from it. The configureHttpConnection() function is designed to do that.   The function opens the URL, then sets it up to send data (POST) to ServiceNow, it then encodes and sets the userid and password and then sets other settings required for SOAP calls.

The other parts of the code are all about building and constructing the SOAP calls (think XML that will be sent over the URL to ServiceNow).

//	Name: serviceNowCreateItem.js
//	Version: 1.0 - 10/22/2015
//	Created by: Tobin Isenberg, CSC
//
//	Description:		This is an example CSC Agility LifecyclePostProcess script that opens a ServiceNow
//				Change Request or Incident based on settings in the script.  It is designed so
//				you can change basic settings at the top of the script and the rest of the script
//				processes the actual ServiceNow request to open a change request or an incident.
//				The script was tested against Agility 9.2.6 but should work for almost any release
//				of Agility.   In the current state, it can be run through rhino to test that 
//				it is able to open items in ServiceNow.  When implementing it inside of Agility,
//				see Optional Changes below for passing Agility specific values to ServiceNow.
//
//	Required Changes:
//				The top of the script contains the URL of the ServiceNow instance, and the 
//				userid and password to use.  Please update all "CHANGE_ME" accordingly.
//
//						var servicenow_url...
//						var user_id...
//						var password...
//
//				The serviceNowRequest type is used to tell the script which type of item is to be
//				created in ServiceNow.  Currently only supports two options.  Either set it to
//				change_request or incident.
//
//						var serviceNowRequestType="change_request";
//
//				The actual fields that will be updated in ServiceNow for a Change Request or an
//				Incident is stored within the fields_name_value variable.  There is one example
//				for a Change Request and one for an Incident.  You must provide the appropriate field
//				name and value pair properly or you will get a generic 500 error returned.  The
//				best way to figure it out is to open a ServiceNow console and during the 'Add' 
//				process, you will see the required fields based on the asterick next to each
//				field name.  You must pass all required fields.   An example incident is shown below.   
//				There is also a commented out Change Request example further down.
//
//						var fields_name_value = [
//							["company", "CSC Internal"],
//							["caller", "Tobin Isenberg"],
//							["priority", "4 - Low"],
//							["short_description", descriptionVal ]   //dynamic value based on variable above
//						];
//
//	Optional Changes:
//				The script contains a small section of code the retrieves some basic information 
//				from CSC's Agility Platform about the item that was just started.  By uncommenting
//				out this code:
//
//						var mapOfVals = agility.getParams();
//						var assetType = mapOfVals.get( 'assetType' );  /* ie: "Instance" */
//						var eventType = mapOfVals.get( 'eventType' );     /* ie: Start, Stop */
//						var agilityItem = agility.params.get("this").getName()
//
//				The script will retrieve details about the item that was started.   A few more lines
//				below that, it sets a varable to be used in fields_name_value to set 
//				the short description.   Only use one of the descriptionVal's.
//
//						var descriptionVal = agilityItem + " " + assetType + ", Action: " + eventType;
//
//

log( "ServiceNow Create Item Script Starting..." );

// URL of ServiceNow instance
	var servicenow_url = "https://CHANGE_ME.service-now.com";

// UserID and Password for ServiceNow
	var user_id = "CHANGE_ME";
	var password = "CHANGE_ME";

//Type of ServiceNow request (support for 'change_request' and 'incident' only at this point)
	var serviceNowRequestType="change_request";


// Retrieve Agility Specific Values to be passed to ServiceNow
// next four lines commented out, CSC Agility specific to retrieve values for short_description value
	// var mapOfVals = agility.getParams();
	// var assetType = mapOfVals.get( 'assetType' );  /* ie: "Instance" */
	// var eventType = mapOfVals.get( 'eventType' );     /* ie: Start, Stop */
	// var agilityItem = agility.params.get("this").getName()

// Set values in Description variable, uncomment only one of the lines begining with: var descriptionVal =...
	// Uses CSC Agility values for short description further down
		// var descriptionVal = agilityItem + " " + assetType + ", Action: " + eventType;
	// Uses generic hardcoded value for short_description further down
		var descriptionVal = "Description Text for ServiceNow"

// ServiceNow fields and values (you must fill in any ServiceNow required fields or script will fail)
// array of field/value pairs, ie: field = category with value = Software'
	// Change Request example
	var fields_name_value = [
		["category", "Software"],
		["priority", "4 - Low"],
		["Impact", "3 - Low"],
		["u_type", "Standard"],
		["short_description", descriptionVal ]   //dynamic value based on variable above
	];

	// Incident Example.
//	var fields_name_value = [
//		["company", "CSC Internal"],
//		["caller", "Tobin Isenberg"],
//		["priority", "4 - Low"],
//		["short_description", descriptionVal ]   //dynamic value based on variable above
//	];



//--------- No Changes Required Below This Line (hopefully) ---------------------

// Type of action, (only supports "insert" at this time)
var method="insert"

// Set values for soap request type 
var serviceNow = null;
switch( serviceNowRequestType )
{
	case 'change_request':
		serviceNow = {
			table_name:"change_request", 
			soapTag: "chan"
		};
		break;
	case 'incident':
		serviceNow = {
			table_name:"incident", 
			soapTag: "inc"
		};
		break;
}

SOAP_SERVICE_URL = servicenow_url + "/" + serviceNow.table_name + ".do?SOAP";
SOAP_ACTION = "http://www.service-now.com/"+ serviceNow.table_name + "/" + method ;

var request =  "<" + serviceNow.soapTag + ":" + method + ">";
for(var i = 0; i < fields_name_value.length; i++)
{
     var name = fields_name_value[i][0];
     var value = fields_name_value[i][1];
     request += "<" +name +">" + value + "</" + name + ">";
}
request += "</" + serviceNow.soapTag + ":" + method +">";

var response = sendRequest(request);

log( "ServiceNow Create Item Script Completed..." );


function sendRequest(request) 
{
     var soapRequest = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:" + serviceNow.soapTag + "='http://www.service-now.com/" + serviceNow.table_name +"'>"
     soapRequest += "<soapenv:Header/>"
     soapRequest += "<soapenv:Body>"
     soapRequest += request
     soapRequest += "</soapenv:Body>>"
     soapRequest += "</soapenv:Envelope>";
     log("SOAP REQUEST: \n" + soapRequest);

     var responseText = sendPost(soapRequest);
     log("SOAP RESPONSE: \n" + responseText);

     return( responseText );
}



function sendPost(soapRequest) 
{
     var conn = configureHttpConnection(soapRequest.length);
     var out = conn.getOutputStream();
     var writer = new Packages.java.io.OutputStreamWriter(out);
     writer.write(soapRequest);
     writer.flush();
     writer.close();

     var response = "";
     var line;
     var inputstream;
     try 
     {
          inputstream = conn.getInputStream();
     }catch( Exception ){
                throw Exception;
          //inputstream = conn.getErrorStream();
     }

     var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(inputstream));
     while ((line = reader.readLine()) != null) 
     {
          response += line;
     }

     reader.close();
     conn.disconnect();
     return response;
}


function configureHttpConnection(length)  
{
     var u = new Packages.java.net.URL(SOAP_SERVICE_URL);
     var connection = u.openConnection();
     connection.setRequestMethod("POST");
     connection.setDoOutput(true);
     var bytes = new java.lang.String( user_id + ":" + password ).getBytes();
     var authorization = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary( bytes );
     connection.setRequestProperty("Authorization", authorization );
     connection.setDoInput(true);

     var soapAction = "\"" + SOAP_ACTION + "\"";
     //log("SOAPAction: " + soapAction);
     connection.setRequestProperty("SOAPAction", soapAction);
     connection.setRequestProperty("Content-Type", "text/xml;charset=UTF8");
     connection.setRequestProperty("Content-Length", "" + length);

     return connection;
}



function log(message) {
	if( typeof agility == 'undefined' )
	{
		java.lang.System.out.print( message );
	}else{
		agility.logger.debug( message );
	}
}


– Tobin Isenberg

Comments

  1. Hoi Tsang says:

    Hi Tobin,

    This articular is great. It gave me idea how to integrate into a different REST APIs. Would you know if there is away we can integrate with websocket endpoint within rhino as well? We have a few rest endpoints are async.

    Regards,
    Hoi

    Liked by 1 person

    • tobinisenberg says:

      Thank you! Rhino is pretty open, in my past job I used pretty much any java package, class, etc. Update a database, send an email, run a command line utility, open a socket and read/write from the stream. I even did an example where I issued tweets when there was an error/event. I like to test in the command line rhino app for specific syntax when converting from java syntax to java script.

      As for Agility and how open rhino is, I imagine it is the same from what I have seen. You just have to kick the tires and give it a try. Remember design is very important. You are potentially creating performance problems, memory problems, etc. When writing, think in large scale; what happens if this is fired 100 times in a row.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: