Collaboration Store, Part XLI

“If opportunity doesn’t knock, build a door.”
Milton Berle

Last time out we started work on the Script Include function that will sync up the data on a Client instance with that of the Host instance. We stopped short of consolidating a couple of cloned functions into a single shared function, but before we get to that effort, we need to add the code that syncs up the application table data. We already wrote the code that syncs up the instance table data, and since this operation is essentially the same process, we should be able lift most of the code from what we have already done.

As we did with the instances, we will need to gather up all of the applications on the Host associated with the instance being processed. This is basically the same process that we went through with the instance table, and the code will be very much the same except for the table and the query.

var applicationList = [];
var applicationGR = new GlideRecord('x_11556_col_store_member_application');
applicationGR.addQuery('provider.name', thisInstance);
applicationGR.query();
while (applicationGR.next()) {
	applicationList.push(applicationGR.getDisplayValue('name'));
}

We will also need to contact the Client instance and fetch all of the applications associated with the current instance being processed that are present on that system. Once again, we can turn to the REST API Explorer to generate an end point URL, and other than the actual URL, the request object needed for fetching the applications is again basically the same as that which we put together to fetch the instances from the Client.

var request  = new sn_ws.RESTMessageV2();
request.setHttpMethod('get');
request.setBasicAuth(this.CSU.WORKER_ROOT + instance, token);
request.setRequestHeader("Accept", "application/json");
request.setEndpoint('https://' + instance + '.service-now.com/api/now/table/x_11556_col_store_member_application?sysparm_fields=name%2Csys_id&sysparm_query=provider%3D' + remoteSysId);

Now that we have build our request object, we need to execute the request and check the results just as we did when gathered up the Client’s list of instances.

var response = request.execute();
if (response.haveError()) {
	gs.error('InstanceSyncUtils.syncApplications - Error returned from attempt to fetch application list: ' + response.getErrorCode() + ' - ' + response.getErrorMessage());
} else if (response.getStatusCode() == '200') {
	var jsonString = response.getBody();
	var jsonObject = {};
	try {
		jsonObject = JSON.parse(jsonString);
	} catch (e) {
		gs.error('InstanceSyncUtils.syncApplications - Unparsable JSON string returned from attempt to fetch application list: ' + jsonString);
	}
	if (Array.isArray(jsonObject.result)) {
		...
	} else {
		gs.error('InstanceSyncUtils.syncApplications - Invalid response body returned from attempt to fetch application list: ' + response.getBody());
	}
} else {
	gs.error('InstanceSyncUtils.syncApplications - Invalid HTTP response code returned from attempt to fetch application list: ' + response.getStatusCode());
}

Assuming that all went well with gathering up all of the data on the Host and the Client, the next thing that we need to do is loop through all of the applications found on the Host for this instance and see if they are present on the Client as well. If not, we will need to push them over, which is where we will get into that code that we cloned that needs to be refactored into a single function so that we can have one process that can serve the two original purposes and our new purpose as well.

for (var i=0; i<applicationList.length; i++) {
	var thisApplication = applicationList[i];
	var remoteAppId = '';
	for (var j=0; j<jsonObject.result.length && remoteAppId == ''; j++) {
		if (jsonObject.result[j].name == thisApplication) {
			remoteAppId = jsonObject.result[j].sys_id;
		}
	}
	if (remoteAppId == '') {
		remoteAppId = this.sendApplication(targetGR, thisApplication, thisInstance, remoteSysId);
	}
	this.syncVersions(targetGR, thisApplication, remoteAppId);
}

This code references two functions that we have not yet created, sendApplication and syncVersions. We should be able to cut and paste the sendInstance function that we created earlier to build the sendApplication function with just a few minor modifications. Similarly, we should be able to clone the syncApplications function to serve as the initial basis of the syncVersions function. At this point, we are assuming that we have collapsed our two cloned functions that send applications over into a single function in the CollaborationStoreUtils Script Include modeled after the publishInstance function that we were using to send over an instance record, and we are assuming that it will return the very same type of object in response.

sendApplication: function(targetGR, thisApplication, thisInstance, remoteSysId) {
	var sysId = '';
	var applicationGR = new GlideRecord('x_11556_col_store_member_application');
	applicationGR.addQuery('name', thisApplication);
	applicationGR.addQuery('provider.name', thisInstance);
	applicationGR.query();
	if (applicationGR.next()) {
		var result = this.CSU.publishApplication(applicationGR, targetGR, remoteSysId);
		if (result.error) {
			gs.error('InstanceSyncUtils.sendApplication - Error occurred attempting to push application ' + thisApplication + ' : ' + result.errorCode + '; ' + result.errorMessage);
		} else if (result.status != 200) {
			gs.error('InstanceSyncUtils.sendApplication - Invalid HTTP response code returned from attempt to push application ' + thisApplication + ' : ' + result.status);
		} else if (!result.obj) {
			gs.error('InstanceSyncUtils.sendApplication - Invalid response body returned from attempt to push application ' + thisApplication + ' : ' + result.body);
		} else {
			sysId = result.obj.sys_id;
		}
	}
	return sysId;
}

Of course, for this to work, we actually have to create a function in CollaborationStoreUtils called publishApplication, but we already have two functions that do that very thing and a publishInstance function to use as a model. It shouldn’t take all that much to collapse all of that together into a single function that will work for everyone, so let’s make that the focus of our next installment.