Adding Detail to the Upgrade History Task

“Ask not that the journey be easy; ask instead that it be worth it.”
John F. Kennedy

With the addition of the Paris Upgrade Center, a new type of Task was introduced, the Upgrade History Task. Whenever you upgrade your instance, issues that require your attention will now produce an Upgrade History Task, which you can assign to the appropriate resources for resolution. This is a nice feature, and fairly well implemented, but the form layout is not quite organized in the way that I would like to see it, and there are some tidbits of data from various other, related tables that I would really like to see all in one place. Another thing that annoys me is that the Upgrade Details Related Link opens up a new window and breaks out of the iFrame, losing the header and sidebar menu. That page has a link back to the Upgrade History Task, and if you click back and forth a few times, suddenly you have all kinds of windows open, and none of them have navigation anymore. I don’t like that.

So, I thought about making a bunch of UI Formatters and rearranging the stock form layout to include all of the information that I like to see when I am working an upgrade issue, but the more upgrades that I work on, the less I like to tinker with stock components. Ultimately, I decided to just add a single UI Action that would pop up a modal dialog box that contained the information that I was looking for. Here are the things that I wanted to see:

  • A standard link to the Skip record that didn’t open up a new window,
  • The name of the affected table,
  • A link to the affected record,
  • When the affected record was last updated,
  • Who last updated the affected record,
  • Any indication that we have dealt with this issue before, and if so,
  • The details of what was done with this the last time that it came up in an upgrade.

None of that is present on the form right now, but I didn’t see any reason that we couldn’t pull it all together from the various sources, so I went to work. It seemed like there would be a bit of database querying to get all of this information, and I didn’t really want all of that on the page itself, so I started out by making myself a little Script Include to house all of major code. I called it UpgradeTaskUtils and created a single function called getAdditionalInfo to pull together all of the data.

var UpgradeTaskUtils = Class.create();
UpgradeTaskUtils.prototype = {
    initialize: function() {
    },

    getAdditionalInfo: function(sysId) {
		var details = {};

		// all of the hard work goes here!

		return details;
	},

	type: 'UpgradeTaskUtils'
};

The first thing that we will need to do, of course, is to use the incoming sysId argument to go out and get the Upgrade History Record, and once we do that, we can then pull out the associated Skip record.

details.taskGR = new GlideRecord('upgrade_history_task');
if (details.taskGR.get(sysId)) {
	details.logGR = details.taskGR.upgrade_detail.getRefRecord();
	if (details.logGR && details.logGR.isValidRecord()) {
		// more code to follow ...
	}
}

Once we know that we have a valid Skip record, the next thing that we will want to do is go get the actual record that has the issue. That’s a little more complicated and uses a table called sys_metadata.

var metaGR = new GlideRecord('sys_metadata');
if (metaGR.get('sys_update_name', details.logGR.file_name.toString())) {
	details.recordGR = new GlideRecord(metaGR.sys_class_name);
	if (details.recordGR.get(metaGR.sys_id)) {
		details.lastRecordUpdate = details.recordGR.getDisplayValue('sys_updated_on');
		details.lastRecordUpdateBy = details.recordGR.getDisplayValue('sys_updated_by');
		// more code to follow ...
	}
}

Since the sys_updated_by fields is just a user_name string and not an actual reference to a User record, if we want to have the details on the User who last updated the record, we will need to go out and fetch that separately.

var userGR = new GlideRecord('sys_user');
if (userGR.get('user_name', details.lastRecordUpdateBy)) {
	details.userName = userGR.getDisplayValue('name');
	details.userSysId = userGR.getUniqueValue();
	details.userLink = '<a href="/sys_user.do?sys_id=' + details.userSysId + '">' + details.userName + '</a>';
}

That takes care of all of the interrelated records involved with this Task, but there is still more work to do if we want any historical data for this same artifact in previous upgrades. Basically, we want to find all of the records in the Upgrade History Task table that reference this same component, except for the one that we already have. We can just do a quick count to start off with, just to see if there is any point in looking any further.

details.previousIssues = 0;
details.prevIssueQuery = 'upgrade_detail.file_name=' + details.taskGR.upgrade_detail.file_name + '^number!=' + details.taskGR.number;
var taskGA = new GlideAggregate('upgrade_history_task');
taskGA.addAggregate('COUNT');
taskGA.addEncodedQuery(details.prevIssueQuery);
taskGA.query();
if (taskGA.next()) {
	details.previousIssues = taskGA.getAggregate('COUNT');
}

Now that we have a count of what’s out there, we can gather up all of the details if the count is greater than zero.

details.prevIssueLink = details.previousIssues;
if (details.previousIssues > 0) {
	details.prevIssueLink = '<a href="/upgrade_history_task_list.do?sysparm_query=' + details.prevIssueQuery + '">' + details.previousIssues + '</a>';
	var taskGR = new GlideRecord('upgrade_history_task');
	taskGR.addEncodedQuery(details.prevIssueQuery);
	taskGR.orderByDesc('sys_created_on');
	taskGR.query();
	if (taskGR.next()) {
		details.previousUpgrade = taskGR.getDisplayValue('upgrade_detail.upgrade_history.to_version');
		details.previousComments = taskGR.getDisplayValue('upgrade_detail.comments');
	}
}

That’s everything that I am looking for right at the moment. I may end up going back one day and tossing in a few more items, but for now, this should do the trick. All together, the new Script Include looks like this:

var UpgradeTaskUtils = Class.create();
UpgradeTaskUtils.prototype = {
    initialize: function() {
    },

    getAdditionalInfo: function(sysId) {
		var details = {};

		details.taskGR = new GlideRecord('upgrade_history_task');
		if (details.taskGR.get(sysId)) {
			details.logGR = details.taskGR.upgrade_detail.getRefRecord();
			if (details.logGR && details.logGR.isValidRecord()) {
				var metaGR = new GlideRecord('sys_metadata');
				if (metaGR.get('sys_update_name', details.logGR.file_name.toString())) {
					details.recordGR = new GlideRecord(metaGR.sys_class_name);
					if (details.recordGR.get(metaGR.sys_id)) {
						details.lastRecordUpdate = details.recordGR.getDisplayValue('sys_updated_on');
						details.lastRecordUpdateBy = details.recordGR.getDisplayValue('sys_updated_by');
						var userGR = new GlideRecord('sys_user');
						if (userGR.get('user_name', details.lastRecordUpdateBy)) {
							details.userName = userGR.getDisplayValue('name');
							details.userSysId = userGR.getUniqueValue();
							details.userLink = '<a href="/sys_user.do?sys_id=' + details.userSysId + '">' + details.userName + '</a>';
						}
					}
				}
			}
			details.previousIssues = 0;
			details.prevIssueQuery = 'upgrade_detail.file_name=' + details.taskGR.upgrade_detail.file_name + '^number!=' + details.taskGR.number;
			var taskGA = new GlideAggregate('upgrade_history_task');
			taskGA.addAggregate('COUNT');
			taskGA.addEncodedQuery(details.prevIssueQuery);
			taskGA.query();
			if (taskGA.next()) {
				details.previousIssues = taskGA.getAggregate('COUNT');
			}
			details.prevIssueLink = details.previousIssues;
			if (details.previousIssues > 0) {
				details.prevIssueLink = '<a href="/upgrade_history_task_list.do?sysparm_query=' + details.prevIssueQuery + '">' + details.previousIssues + '</a>';
				var taskGR = new GlideRecord('upgrade_history_task');
				taskGR.addEncodedQuery(details.prevIssueQuery);
				taskGR.orderByDesc('sys_created_on');
				taskGR.query();
				if (taskGR.next()) {
					details.previousUpgrade = taskGR.getDisplayValue('upgrade_detail.upgrade_history.to_version');
					details.previousComments = taskGR.getDisplayValue('upgrade_detail.comments');
				}
			}
		}

		return details;
	},

	type: 'UpgradeTaskUtils'
};

Now we need put all of this data on page that we can use for our modal pop-up. I created a new UI Page called upgrade_history_task_info, and started it out by calling our new Script Include to obtain the data.

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
	<g:evaluate var="jvar_not_used">
var utu = new UpgradeTaskUtils();
var obj = utu.getAdditionalInfo(RP.getWindowProperties().get('task_sys_id'));
obj.lastRecordUpdate;		
	</g:evaluate>
	<div style="padding: 20px;">
		<!-- display grid goes here -->
	</div>
</j:jelly>

To format the data, I just used a basic HTML table, with one column for the labels and another for the data.

<table>
	<tr>
		<th style="padding: 5px;">Issue Details:</th>
		<td style="padding: 5px;"><a href="/${obj.logGR.getTableName()}.do?sys_id=${obj.logGR.getUniqueValue()}">${obj.logGR.getDisplayValue()}</a></td>
	</tr>
	<tr>
		<th style="padding: 5px;">Affected Table:</th>
		<td style="padding: 5px;">${obj.recordGR.getLabel()} (${obj.recordGR.getTableName()})</td>
	</tr>
	<tr>
		<th style="padding: 5px;">Affected Record:</th>
		<td style="padding: 5px;"><a href="/${obj.recordGR.getTableName()}.do?sys_id=${obj.recordGR.getUniqueValue()}">${(obj.recordGR.getDisplayValue() > ''?obj.recordGR.getDisplayValue():obj.recordGR.getUniqueValue())}</a></td>
	</tr>
	<tr>
		<th style="padding: 5px;">Record Last Updated:</th>
		<td style="padding: 5px;">${obj.recordGR.getDisplayValue('sys_updated_on')}</td>
	</tr>
	<tr>
		<th style="padding: 5px;">Record Last Updated By:</th>
		<td style="padding: 5px;"><g:no_escape>${(obj.userSysId > ''?obj.userLink:obj.recordGR.getDisplayValue('sys_updated_by'))}</g:no_escape></td>
	</tr>
	<tr>
		<th style="padding: 5px;">Previous Upgrade Issues:</th>
		<td style="padding: 5px;"><g:no_escape>${obj.prevIssueLink}</g:no_escape></td>
	</tr>
	<j:if test="${obj.previousIssues > 0}">
		<tr>
			<th style="padding: 5px;">Last Upgrade w/Issue:</th>
			<td style="padding: 5px;">${obj.previousUpgrade}</td>
		</tr>
		<tr>
			<th style="padding: 5px;">Last Upgrade Comments:</th>
			<td style="padding: 5px;">${obj.previousComments}</td>
		</tr>
	</j:if>
</table>

Now we have our data and we have it laid out nicely on a page, all that’s left to do is to pop it up on the screen. For that, we will need to build a UI Action. I called mine Additional Info, linked it to the Upgrade History Task table, and gave it the following onClick script:

function openAdditionalInfo() {
	var dialog = new GlideDialogWindow('upgrade_history_task_info');
	dialog.setSize(600, 600);
	dialog.setTitle('Additional Info');
	dialog.setPreference('task_sys_id', NOW.sysId);
	dialog.render();
}

That’s pretty much all there is to that. We still need to pull it up and click on it and see what happens, but assuming that all goes well, this exercise should have produced a nice little tool to make plodding through the skipped records in an upgrade just a little bit easier.

Additional Info modal pop-up on the Upgrade History Task form

Nice! Now all we need to do is gather up all of the parts and stuff them into an Update Set.