Collaboration Store, Part XXVI

“It’s not foresight or hindsight we need. We need sight, plain and simple. We need to see what is right in front of us.”
Gordon Atkinson

Last time, we completed the final step in the publication process for apps published on a Host instance. For those apps published on a Client instance, however, there is still more work to do. Everything that we created locally on the instance where the app is being published needs to be transferred over to the Host. To do that, we can utilize the built-in REST API.

The first thing that we will want to do is to fetch the GlideRecord for the app to obtain all of the data to send over. As we have done in other steps, we will check to make sure that we have obtained the record and report an error if we did not.

var mbrAppGR = new GlideRecord('x_11556_col_store_member_application');
if (mbrAppGR.get(answer.mbrAppId)) {
	...
} else {
	answer = this.processError(answer, 'Invalid Member Application sys_id: ' + answer.appSysId);
}

The next thing that we will want to do is to check to see if the app exists on the Host. Although we have already determined whether or not this app is new to the local instance earlier in the process, we will still want to double check to make sure that there isn’t already a version sitting on the Host for some unknown reason. If we find it, we will want to update it; otherwise, we will want to add it. To find the app on the remote Host, we will use both the name of the app and the name of the instance as query arguments (other instances may have published an app with the same name, but we would not want to update any of those). That’s a fairly standard REST API HTTP get operation.

var host = gs.getProperty('x_11556_col_store.host_instance');
var token = gs.getProperty('x_11556_col_store.active_token');
var thisInstance = gs.getProperty('instance_name');
var request  = new sn_ws.RESTMessageV2();
request.setHttpMethod('get');
request.setBasicAuth(this.WORKER_ROOT + host, token);
request.setRequestHeader("Accept", "application/json");
request.setEndpoint('https://' + host + '.service-now.com/api/now/table/x_11556_col_store_member_application?sysparm_fields=sys_id&sysparm_query=provider.instance%3D' + thisInstance + '%5Ename%3D' + encodeURIComponent(mbrAppGR.getDisplayValue('name')));
var response = request.execute();
if (response.haveError()) {
	answer = this.processError(answer, 'Error returned from Host instance: ' + response.getErrorCode() + ' - ' + response.getErrorMessage());
} else if (response.getStatusCode() == '200') {
	...
} else {
	answer = this.processError(answer, 'Invalid HTTP Response Code returned from Host instance: ' + response.getStatusCode());
}

The HTTP response code should be 200 whether or not any records were returned. To determine if there is a record present on the Host instance, we need to parse the returned JSON string and check the size of the array of records returned.

var jsonString = response.getBody();
var jsonObject = {};
try {
	jsonObject = JSON.parse(jsonString);
} catch (e) {
	answer = this.processError(answer, 'Unparsable JSON string returned from Host instance: ' + jsonString);
}

If we are updating an existing record, the HTTP Method will be a PUT and the URL will include the sys_id of the record. If we are inserting a new record, then the HTTP Method will be a POST. Other than that, the data that we will be sending to the Host instance will be virtually the same, so we can start to build that up before we make the determination as to which method we will use.

var payload = {};
payload.name = mbrAppGR.getDisplayValue('name');
payload.description = mbrAppGR.getDisplayValue('description');
payload.current_version = mbrAppGR.getDisplayValue('current_version');
payload.active = 'true';
request  = new sn_ws.RESTMessageV2();
request.setBasicAuth(this.WORKER_ROOT + host, token);
request.setRequestHeader("Accept", "application/json");

Now we can check the size of the array of records returned and handle the things that will be different depending on whether or not this is a new record on the Host.

if (jsonObject.result && jsonObject.result.length > 0) {
	answer.hostAppId = jsonObject.result[0].sys_id;
	request.setHttpMethod('put');
	request.setEndpoint('https://' + host + '.service-now.com/api/now/table/x_11556_col_store_member_application/' + answer.hostAppId);
} else {
	request.setHttpMethod('post');
	request.setEndpoint('https://' + host + '.service-now.com/api/now/table/x_11556_col_store_member_application');
	payload.provider = thisInstance;
}
request.setRequestBody(JSON.stringify(payload, null, '\t'));

Now that we have everything all set up, all this is left to do is to execute the method and check the results.

response = request.execute();
if (response.haveError()) {
	answer = this.processError(answer, 'Error returned from Host instance: ' + response.getErrorCode() + ' - ' + response.getErrorMessage());
} else if (response.getStatusCode() != 200 && response.getStatusCode() != 201) {
	answer = this.processError(answer, 'Invalid HTTP Response Code returned from Host instance: ' + response.getStatusCode());
} else {
	jsonString = response.getBody();
	jsonObject = {};
	try {
		jsonObject = JSON.parse(jsonString);
	} catch (e) {
		answer = this.processError(answer, 'Unparsable JSON string returned from Host instance: ' + jsonString);
	}
	if (!answer.error) {
		answer.hostAppId = jsonObject.result.sys_id;
	}
}

If all goes well, the application record will now be present on the Host instance, which will only leave us with two more things to do, add a new version record to the Host instance and then attach the Update Set XML to the version record. Here is the full function for this step in its entirety.

processPhase5: function(answer) {
	var mbrAppGR = new GlideRecord('x_11556_col_store_member_application');
	if (mbrAppGR.get(answer.mbrAppId)) {
		var host = gs.getProperty('x_11556_col_store.host_instance');
		var token = gs.getProperty('x_11556_col_store.active_token');
		var thisInstance = gs.getProperty('instance_name');
		var request  = new sn_ws.RESTMessageV2();
		request.setHttpMethod('get');
		request.setBasicAuth(this.WORKER_ROOT + host, token);
		request.setRequestHeader("Accept", "application/json");
		request.setEndpoint('https://' + host + '.service-now.com/api/now/table/x_11556_col_store_member_application?sysparm_fields=sys_id&sysparm_query=provider.instance%3D' + thisInstance + '%5Ename%3D' + encodeURIComponent(mbrAppGR.getDisplayValue('name')));
		var response = request.execute();
		if (response.haveError()) {
			answer = this.processError(answer, 'Error returned from Host instance: ' + response.getErrorCode() + ' - ' + response.getErrorMessage());
		} else if (response.getStatusCode() == '200') {
			var jsonString = response.getBody();
			var jsonObject = {};
			try {
				jsonObject = JSON.parse(jsonString);
			} catch (e) {
				answer = this.processError(answer, 'Unparsable JSON string returned from Host instance: ' + jsonString);
			}
			if (!answer.error) {
				var payload = {};
				payload.name = mbrAppGR.getDisplayValue('name');
				payload.description = mbrAppGR.getDisplayValue('description');
				payload.current_version = mbrAppGR.getDisplayValue('current_version');
				payload.active = 'true';
				request  = new sn_ws.RESTMessageV2();
				request.setBasicAuth(this.WORKER_ROOT + host, token);
				request.setRequestHeader("Accept", "application/json");
				if (jsonObject.result && jsonObject.result.length > 0) {
					answer.hostAppId = jsonObject.result[0].sys_id;
					request.setHttpMethod('put');
					request.setEndpoint('https://' + host + '.service-now.com/api/now/table/x_11556_col_store_member_application/' + answer.hostAppId);
				} else {
					request.setHttpMethod('post');
					request.setEndpoint('https://' + host + '.service-now.com/api/now/table/x_11556_col_store_member_application');
					payload.provider = thisInstance;
				}
				request.setRequestBody(JSON.stringify(payload, null, '\t'));
				response = request.execute();
				if (response.haveError()) {
					answer = this.processError(answer, 'Error returned from Host instance: ' + response.getErrorCode() + ' - ' + response.getErrorMessage());
				} else if (response.getStatusCode() != 200 && response.getStatusCode() != 201) {
					answer = this.processError(answer, 'Invalid HTTP Response Code returned from Host instance: ' + response.getStatusCode());
				} else {
					jsonString = response.getBody();
					jsonObject = {};
					try {
						jsonObject = JSON.parse(jsonString);
					} catch (e) {
						answer = this.processError(answer, 'Unparsable JSON string returned from Host instance: ' + jsonString);
					}
					if (!answer.error) {
						answer.hostAppId = jsonObject.result.sys_id;
					}
				}
			}
		} else {
			answer = this.processError(answer, 'Invalid HTTP Response Code returned from Host instance: ' + response.getStatusCode());
		}
	} else {
		answer = this.processError(answer, 'Invalid Member Application sys_id: ' + answer.appSysId);
	}

	return answer;
},

Next time, we will code out the insertion of the version record, which should be very similar to this step, although it should be a little simpler since we do not have to check to see if a record exists or not. Version records are always new records for each new version published. That should simplify things quite a bit.