“The secret of change is to focus all of your energy not on fighting the old, but on building the new”
— Socrates
After running my LinkReferencedTasks Business Rule for a while, it has become apparent that there was a flaw in my approach. Whenever someone separates different task numbers with a special character, neither one gets picked up in the process. For example, if you enter something like REQ0010005/RITM0010009, the process that converts all special characters to an empty string ends up converting that to REQ0010005RITM0010009, which starts with REQ, but is not a valid Task number. A little Corrective Maintenance should solve that problem. Let’s convert this:
text = text.replace(/\n/g, ' ');
text = text.replace(/[^A-Z0-9 ]/g, '');
… to this:
text = text.replace(/[^A-Z0-9 ]/g, ' ');
Originally, I had converted all line feeds to spaces and then all characters that were not letters, numbers, or spaces to an empty string. Once I decided to change all characters that were not letters, numbers, or spaces to a space, I no longer needed the line above, as line feeds fall into that same category. That solved that problem.
However, while I was in there, I decided to do a little Perfective Maintenance as well. By changing all of the special characters to single spaces, I thought that I might end up with several spaces in a row, which would result in one or more empty strings in my array of words. To screen those out, I thought about discarding words with a length of zero, but then it occurred to me that short words of any kind will not be task numbers, so I settled on an arbitrary minimum length of 6 instead. Now the code that unduplicates the list of words looks like this:
var unduplicated = {};
for (var i=0; i<words.length; i++) {
var thisWord = words[i];
if (thisWord.length > 5) {
unduplicated[thisWord] = thisWord;
}
}
This should reduce the clutter quite a bit and minimize the number of words that need to be checked to see if they start with one of the specified prefixes.
And finally, I added an Enhancement, which I had actually thought about earlier, but didn’t implement. This enhancement allows you to specify the relationship type instead of just accepting the hard-coded Investigates relationship that I had put in the original. For backward compatibility, I did not want to make that mandatory, so I kept that as a default for those implementations that did not want to specify their own. The new version of the Script Include now looks like this:
var TaskReferenceUtils = Class.create();
TaskReferenceUtils.prototype = {
initialize: function() {
},
linkReferencedTasks: function(taskGR, prefix, relationship) {
if (!relationship) {
relationship = 'd80dc65b0a25810200fe91a7c64e9cac';
}
var text = taskGR.short_description + ' ' + taskGR.description;
text = text.toUpperCase();
text = text.replace(/[^A-Z0-9 ]/g, ' ');
var words = text.split(' ');
var unduplicated = {};
for (var i=0; i<words.length; i++) {
var thisWord = words[i];
if (thisWord.length > 5) {
unduplicated[thisWord] = thisWord;
}
}
for (var word in unduplicated) {
for (var x in prefix) {
if (word.startsWith(prefix[x])) {
this._findTask(word, taskGR.getUniqueValue(), relationship);
}
}
}
},
_findTask: function(number, child) {
var taskGR = new GlideRecord('task');
if (taskGR.get('number', number)) {
this._documentRelationship(taskGR.getUniqueValue(), child, relationship);
}
},
_documentRelationship: function(parent, child, relationship) {
var relGR = new GlideRecord('task_rel_task');
relGR.addQuery('parent', parent);
relGR.addQuery('child', child);
relGR.query();
if (!relGR.next()) {
relGR.initialize();
relGR.parent = parent;
relGR.child = child;
relGR.type = relationship;
relGR.insert();
}
},
type: 'TaskReferenceUtils'
};
So, a little fix here, a little improvement there, and a brand new feature over there, and suddenly we have a new version better than the last. Stuff’s getting better. Stuff’s getting better every day. Here’s the improved Update Set.