Easily Move Customer Updates Between Update Sets

As ServiceNow developers, we often encounter scenarios where we need to migrate configurations from one update set to another. Traditionally, this involves manually editing each customer update record to change its update set, which can be tedious and time-consuming, especially when dealing with a large number of updates.

This article demonstrates how to streamline this process by creating a UI Action that allows you to move multiple customer updates between update sets with a single click.

Problem Statement
When you make configurations in your instance, there are times when you need to move these updates to a different update set. The conventional method requires opening each customer update record individually and changing the update set field to the desired update set. This method becomes impractical when dealing with numerous updates.

Solution
To overcome this challenge, you can create a UI Action that facilitates the movement of multiple customer updates to another update set in one go. This approach not only saves time but also reduces the risk of manual errors.

To begin, navigate to System Definition > UI Actions and create a new UI Action:

UI Action
Name: Move Updates to Current US
Table: sys_update_xml
Action Name: move_updates_to_current_us
Show insert: true
Show update: true
Client: true
List v2 Compatible: true
List Choice: true
Onclick: showConfirmDialog()
Script:

function showConfirmDialog() {

	var entries = g_list.getChecked();
	if (!entries || entries.length == 0)
		return;

	// this function runs when the modal close
	var callback = function () {

		// To send parameters to the server script we should use GlideAjax
		var ga = new GlideAjax('MoveUpdatesToCurrentUS');
		ga.addParam('sysparm_name', 'moveEntries');
		ga.addParam('sysparm_entry_ids', entries);

		// GlideAjax callback to refresh the related list
		var afterMove = function () {
			GlideList2.get(g_form.getTableName() + '.sys_update_xml.update_set').setFilterAndRefresh('');
		};

		ga.getXMLAnswer(afterMove.bind(this));
		return true;
	};

	var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;
	var dialog = new dialogClass('glide_confirm_standard');
	dialog.setTitle(new GwtMessage().getMessage('Confirmation'));
	dialog.setPreference('warning', true);
	dialog.setPreference('title', new GwtMessage().getMessage('move_update_set_entries'));
	dialog.setPreference('onPromptComplete', callback.bind(this));
	dialog.render();
	
}

Two points I would like to highlight in this script.
– As we need to pass the selected records as a parameter to the server script, we need to use GlideAjax.
– For the callback function I needed to update only the related list. Initially I could only update the entire record but a quick search here on the portal led me to an article explaining exactly what I needed.

For the modal message to be displayed correctly, we need to create a record in the sys_ui_message table with the text we want.  Navigate to System UI > Messages and create a new message:

Now we need to create the Script Include that will perform all the necessary validations and actions. Navigate to System Definition> Script Includes and create a new script:

Script Include
Name: MoveUpdatesToCurrentUS
Accessible from: All application scopes
Client callable: true
Active: true
Description: Ajax helper class for helping the [Move Updates to Current US] UI action
Script:

var MoveUpdatesToCurrentUS = Class.create();

MoveUpdatesToCurrentUS.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    moveEntries: function () {

        var arrMoved = [];
        var objMoved = {};
        var arrNotMoved = [];
        var objNotMoved = {};
        var currUS_ID = '';
        var customUpdateLink = '';
        var customUpdateType = '';
        var baseURL = gs.getProperty('glide.servlet.uri');

        //create a link to the syslog to return the logs created by this SI on last 15 minutes 
        var sysLogQuery = 'sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes()^messageLIKEMoveUpdatesToCurrentUS';
        sysLogQuery = encodeURIComponent(sysLogQuery); //https://www.w3schools.com/jsref/jsref_encodeuricomponent.asp
        var sysLogURL = baseURL + 'syslog_list.do?sysparm_query=' + sysLogQuery;
        
        // get the user current US
        var currUS = gs.getPreference('sys_update_set');
        var grUS = new GlideRecord('sys_update_set');		

        if (grUS.get(currUS)) {
            currUS_ID = grUS.getUniqueValue();
        } else {
            gs.info('[MoveUpdatesToCurrentUS] Error: Unable to get Current US.');
            gs.addErrorMessage('There was an error. Please review the <a href="http://'%20+%20sysLogURL%20+%20'">system logs</a> for more details.');
            return false;
        }

        // get the variable sent by the UI Action
        var entries = this.getParameter('sysparm_entry_ids');

        //get all selected custom updates
        var customUpGr = new GlideRecord('sys_update_xml');
        customUpGr.addEncodedQuery('sys_idIN' + entries);
        customUpGr.query();

        while (customUpGr.next()) {
            
            //check if the user can write in the table
            if (customUpGr.canWrite()) {

                //create a link to the current update and get its type
                customUpdateLink = baseURL + 'sys_update_xml.do?sys_id=' + customUpGr.getValue('sys_id');
                customUpdateType = customUpGr.getValue('type');

                // check if it trying to move do a diferent US
                if (customUpGr.getValue('update_set') == currUS_ID) {

                    objNotMoved = {
                        sys_id: customUpGr.getUniqueValue(),
                        reasonHTML: 'Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> is already in your currently US.',
                        reasonText: 'Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - is already in your currently US.'
                    };

                    arrNotMoved.push(objNotMoved);

                } else {

                    // move the customer update to de current US
                    try {

                        customUpGr.setValue('update_set', currUS);
                        customUpGr.update();
                        
                        objMoved = {
                            sys_id: customUpGr.getUniqueValue(),
                            reasonHTML: 'Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> was moved to your currently US.',
                            reasonText: 'Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - was moved to your currently US.'
                        };

                        arrMoved.push(objMoved);

                    } catch (e) {

                        objNotMoved = {
                            sys_id: customUpGr.getUniqueValue(),
                            reasonHTML: 'Unable to move the Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> due: ' + e,
                            reasonText: 'Unable to move the Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - due: ' + e
                        };
    
                        arrNotMoved.push(objNotMoved);

                    }

                }				

            } else {

                objNotMoved = {
                    sys_id: customUpGr.getUniqueValue(),
                    reasonHTML: 'Unable to move the Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> due to ACL restrictions.',
                    reasonText: 'Unable to move the Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - due to ACL restrictions.'			
                };

                arrNotMoved.push(objNotMoved);

            }			

        }

        // create 2 messages: one to the syslog and one to show to user
        var sysLogMessage = '[MoveUpdatesToCurrentUS]' + '\r\r';

        if (arrMoved.length > 0) {
            
            var successScreenMessage = 'Move Updates to Current US Action' + '<br><br>';

            sysLogMessage += 'Custom Updates moved:\r';
            successScreenMessage += 'Custom Updates moved:<br>';

            for (var i = 0; i < arrMoved.length; i++) {
                sysLogMessage += arrMoved[i].reasonText + '\r';
                successScreenMessage += arrMoved[i].reasonHTML + '<br>';
            }

            gs.addInfoMessage(successScreenMessage);

        }

        if (arrNotMoved.length > 0) {

            var failureScreenMessage = 'Move Updates to Current US Action' + '<br><br>';

            sysLogMessage += '\r\rCustom Updates NOT moved:\r';
            failureScreenMessage += 'Custom Updates NOT moved:<br>';

            for (var x = 0; x < arrNotMoved.length; x++) {
                sysLogMessage += arrNotMoved[x].reasonText + '\r';
                failureScreenMessage += arrNotMoved[x].reasonHTML + '<br>';
            }

            gs.addErrorMessage(failureScreenMessage);

        }		

        gs.info(sysLogMessage);
        
        gs.addInfoMessage('Customer Update(s) transfer has been completed. Please review the <a href="http://'%20+%20sysLogURL%20+%20'">system logs</a> for more details.');

        return true;

    },

    type: 'MoveUpdatesToCurrentUS'
});

Using

  • Open the update set from which you want to move few customer updates
  • Go to the Customer Updates Related List
  • Select all the updates you want to move
  • Click on [Move Updates to Current US] in the actions dropdown menu

  • If everything works fine all selected customer updates have been moved to your Current US and you will see a message – simple as that.

  • If any problems occur during the transfer, you will be notified.

  • Everything will be recorded in syslog

Benefits

  • Efficiency: Moves multiple updates simultaneously, significantly reducing the time required.
  • Accuracy: Minimizes the risk of errors associated with manual updates.
  • Usability: Provides a straightforward and user-friendly method for managing update sets.

 

By implementing this UI Action, you can enhance your productivity and ensure a smoother workflow when managing update sets in ServiceNow. This method is particularly beneficial in complex development environments where rapid and accurate configuration management is essential.

Date Posted:

July 26, 2024

Share This:

2 Comments

  1. Kyryl July 27, 2024 at 7:25 pm

    Hi, great article! One recommendation, if you are moving sys_update_xml records between update sets – consider using .autoSysFields(false). Otherwise it might seem that the time you moved the update was the time you modified the application file. This might become an issue if you have batched update sets or have several developers working on the same application file.

    • Thiago Pereira July 27, 2024 at 7:54 pm

      Hi Kyryl! Great point! Thanks for the comment!

Comments are closed.

Categories

Tags

Loading

Fresh Content
Direct to Your Inbox

Just add your email and hit subscribe to stay informed.