The Zurich release has arrived! Interested in new features and functionalities? Click here for more

catalog item

Rishabh_N
Tera Contributor

Hi everyone,

I have a catalog item for group retirement. Before retiring a group, I need to check its dependencies.
I created a Script Include to check dependencies (like Business Rules, ACLs, UI Actions, Client Scripts, etc.) and used it in a workflow Run Script activity  it works fine, but it’s hardcoded and limited.

My question is if a group has a lot of dependencies (some that we don’t even know), how can we identify them dynamically? Since we can’t write individual methods for each dependency in the Script Include, what’s the best approach or best practice in real-time projects?

2 ACCEPTED SOLUTIONS

@Rishabh_N - did you check?

Glad I could help! If this solved your issue, please mark it as Helpful and Accept as Solution so others can benefit too.*****Chavan A.P. | Technical Architect | Certified Professional*****

View solution in original post

@Rishabh_N Custom table is not necessary, what is important here is that someone take care of dependencies before deleting/deactivating the group.

What you did is absolutely fine and will work in real time as well.

 

One more thing you need to add in your catalog task description or maybe variable is that anyone who is working on the task should reassign the task based record( inc/prb/change/sc_task) or any other module you are using to some active group or get these records closed before deactivating. This step can also be automated in your workflow with simple glide on task table and querying open records with existing group and replacing that, but its totally upto your wish , how you want to implement this. 


Raghav
MVP 2023
LinkedIn

View solution in original post

8 REPLIES 8

Thanks a lot for your detailed reply @RaghavSh. That makes sense , i will definitely try out your suggestion.

Ajay_Chavan
Kilo Sage

Hello Rishabh, here is the script include that can cover all possible scenario, if you find any new scenario just add new check in the same:

 

var SimpleGroupChecker = Class.create();
SimpleGroupChecker.prototype = {
    
    checkGroupDependencies: function(groupSysId) {
        var dependencies = [];
        var groupName = this._getGroupName(groupSysId);
        
        if (!groupName) {
            return { success: false, message: 'Group not found' };
        }
        
        // Step 1: Check direct reference fields
        this._checkDirectReferences(groupSysId, dependencies);
        
        // Step 2: Check script content
        this._checkScriptReferences(groupSysId, groupName, dependencies);
        
        // Step 3: Check workflows
        this._checkWorkflows(groupSysId, groupName, dependencies);
        
        return {
            success: true,
            groupName: groupName,
            canRetire: dependencies.length === 0,
            totalDependencies: dependencies.length,
            dependencies: dependencies
        };
    },
    
    // Check common tables with assignment_group fields
    _checkDirectReferences: function(groupSysId, dependencies) {
        var tablesToCheck = [
            'incident', 'change_request', 'sc_request', 'sc_task',
            'problem', 'sys_user_grmember', 'cmn_schedule'
        ];
        
        for (var i = 0; i < tablesToCheck.length; i++) {
            var tableName = tablesToCheck[i];
            
            if (this._tableExists(tableName)) {
                var gr = new GlideRecord(tableName);
                
                // Try assignment_group field first
                if (this._fieldExists(tableName, 'assignment_group')) {
                    gr.addQuery('assignment_group', groupSysId);
                }
                // For group membership table
                else if (tableName === 'sys_user_grmember') {
                    gr.addQuery('group', groupSysId);
                }
                // For schedule table
                else if (tableName === 'cmn_schedule') {
                    gr.addQuery('group', groupSysId);
                }
                
                gr.setLimit(5); // Limit results for performance
                gr.query();
                
                while (gr.next()) {
                    dependencies.push({
                        table: tableName,
                        record: gr.getDisplayValue(),
                        type: this._getTableType(tableName),
                        link: gr.getLink()
                    });
                }
            }
        }
    },
    
    // Check scripts for group references
    _checkScriptReferences: function(groupSysId, groupName, dependencies) {
        var scriptTables = [
            'sys_script',        // Business Rules
            'sys_script_client', // Client Scripts
            'sys_ui_action',     // UI Actions
            'sys_security_acl'   // ACLs
        ];
        
        for (var i = 0; i < scriptTables.length; i++) {
            var tableName = scriptTables[i];
            
            if (this._tableExists(tableName)) {
                var gr = new GlideRecord(tableName);
                gr.addQuery('active', true);
                
                // Search in script field for group sys_id or name
                var query = 'scriptCONTAINS' + groupSysId + '^ORscriptCONTAINS' + groupName;
                if (tableName === 'sys_security_acl') {
                    query += '^ORconditionCONTAINS' + groupSysId + '^ORconditionCONTAINS' + groupName;
                }
                
                gr.addEncodedQuery(query);
                gr.setLimit(3);
                gr.query();
                
                while (gr.next()) {
                    dependencies.push({
                        table: tableName,
                        record: gr.getDisplayValue(),
                        type: this._getTableType(tableName),
                        link: gr.getLink()
                    });
                }
            }
        }
    },
    
    // Check workflows
    _checkWorkflows: function(groupSysId, groupName, dependencies) {
        if (this._tableExists('wf_activity')) {
            var gr = new GlideRecord('wf_activity');
            gr.addQuery('active', true);
            gr.addEncodedQuery('varsCONTAINS' + groupSysId + '^ORvarsCONTAINS' + groupName);
            gr.setLimit(3);
            gr.query();
            
            while (gr.next()) {
                dependencies.push({
                    table: 'wf_activity',
                    record: gr.getDisplayValue(),
                    type: 'Workflow',
                    link: gr.getLink()
                });
            }
        }
    },
    
    // Helper functions
    _getGroupName: function(groupSysId) {
        var gr = new GlideRecord('sys_user_group');
        if (gr.get(groupSysId)) {
            return gr.getValue('name');
        }
        return null;
    },
    
    _tableExists: function(tableName) {
        return GlideTableDescriptor.tableExists(tableName);
    },
    
    _fieldExists: function(tableName, fieldName) {
        return GlideTableDescriptor.fieldExists(tableName, fieldName);
    },
    
    _getTableType: function(tableName) {
        var types = {
            'incident': 'Incident',
            'change_request': 'Change Request',
            'sc_request': 'Service Request',
            'sc_task': 'Catalog Task',
            'problem': 'Problem',
            'sys_user_grmember': 'Group Member',
            'cmn_schedule': 'Schedule',
            'sys_script': 'Business Rule',
            'sys_script_client': 'Client Script',
            'sys_ui_action': 'UI Action',
            'sys_security_acl': 'Access Control',
            'wf_activity': 'Workflow'
        };
        return types[tableName] || tableName;
    },
    
    type: 'SimpleGroupChecker'
};

 

Now run below script in Background script for specific group:

 

// Test a specific group by name
var groupName = 'IT Support'; // Change this to your group name

var groupGR = new GlideRecord('sys_user_group');
if (groupGR.get('name', groupName)) {
    
    // Call the Script Include
    var checker = new SimpleGroupChecker();
    var result = checker.checkGroupDependencies(groupGR.getUniqueValue());
    
    // Simple output
    gs.print('Group: ' + groupName);
    gs.print('Can retire: ' + (result.canRetire ? 'YES' : 'NO'));
    
    if (!result.canRetire) {
        gs.print('Reason: ' + result.totalDependencies + ' dependencies found');
        
        // List first 3 dependencies
        for (var i = 0; i < Math.min(3, result.dependencies.length); i++) {
            gs.print('- ' + result.dependencies[i].type + ': ' + result.dependencies[i].record);
        }
    }
} else {
    gs.print('Group "' + groupName + '" not found');
}

 

Glad I could help! If this solved your issue, please mark it as Helpful and Accept as Solution so others can benefit too.*****Chavan A.P. | Technical Architect | Certified Professional*****

@Rishabh_N - did you check?

Glad I could help! If this solved your issue, please mark it as Helpful and Accept as Solution so others can benefit too.*****Chavan A.P. | Technical Architect | Certified Professional*****

hi Chavan

 

I copied this script include and executed the background script.

 

It is very interesting and kudus to the effort you put in!

 

For a Yokohama PDI instance, the following changes need to be done.

 

1. In the function _checkWorkFlows, the script checks if active is true - there is no field "active" in wf_activity. So, the line "gr.addQuery('active', true)" throws invalid query.

QueryEventLogger: Invalid query detected, please check logs for details [Unknown field active in table wf_activity]

2. When the script checks for existence of table and fields using GlideTableDescriptor API, I get error for using the methods "

tableExists()" and "fieldExists()".
In Yokohama, the GlideTableDescriptor offers the methods get(<tablename>) and 
isValidField(<fieldName>) which can be used for checking validity.
 
Again, thank you so much. It is an eye opener.
 
Best Regards