Client Scripts (1)
Dynamic Field Population on Load
Automatically populate fields based on user information when form loads
function onLoad() {
// Get current user's department and location
var userDept = g_user.department;
var userLocation = g_user.location;
// Set caller field to current user if empty
if (g_form.isNewRecord() && g_form.getValue('caller_id') == '') {
g_form.setValue('caller_id', g_user.userID);
}
// Auto-populate location from user's location
if (userLocation && g_form.getValue('location') == '') {
g_form.setValue('location', userLocation);
}
// Set assignment group based on category
var category = g_form.getValue('category');
if (category == 'hardware' && userDept) {
g_form.setValue('assignment_group', 'Hardware Support');
}
}
Configuration:
- Add to Incident form
- Ensure user has read access to sys_user table
- Test with different user roles
Business Rules (1)
Auto-assign Tasks Based on Priority
Automatically assign tasks to appropriate groups based on priority and category
// Auto-assignment business rule
(function executeRule(current, previous /*null when async*/) {
// Only run on insert and when assignment_group is empty
if (!current.isNewRecord() && !current.assignment_group.changes()) {
return;
}
var priority = current.getValue('priority');
var category = current.getValue('category');
// High priority items go to specialized teams
if (priority == '1' || priority == '2') {
if (category == 'software') {
current.assignment_group = getGroupSysId('Application Support');
} else if (category == 'hardware') {
current.assignment_group = getGroupSysId('Hardware Support');
} else {
current.assignment_group = getGroupSysId('IT Service Desk');
}
}
function getGroupSysId(groupName) {
var gr = new GlideRecord('sys_user_group');
gr.addQuery('name', groupName);
gr.query();
if (gr.next()) {
return gr.getUniqueValue();
}
return '';
}
})(current, previous);
Configuration:
- Table: task
- When: before
- Order: 100
- Active: true
Script Includes (2)
REST API Integration Helper
Centralized utility for making outbound REST API calls with error handling and logging
var RESTIntegrationHelper = Class.create();
RESTIntegrationHelper.prototype = {
initialize: function() {
this.timeout = 30000; // 30 seconds default timeout
this.maxRetries = 3;
},
/**
* Make a REST API call with built-in retry logic and error handling
* @param {Object} config - Configuration object
* @param {string} config.url - API endpoint URL
* @param {string} config.method - HTTP method (GET, POST, PUT, DELETE)
* @param {Object} config.headers - Request headers
* @param {string} config.body - Request body (for POST/PUT)
* @param {number} config.timeout - Request timeout in milliseconds
* @returns {Object} Response object with status, body, and headers
*/
makeRequest: function(config) {
var request = new sn_ws.RESTMessageV2();
request.setEndpoint(config.url);
request.setHttpMethod(config.method || 'GET');
// Set headers
if (config.headers) {
for (var header in config.headers) {
request.setRequestHeader(header, config.headers[header]);
}
}
// Set body for POST/PUT requests
if (config.body && (config.method == 'POST' || config.method == 'PUT')) {
request.setRequestBody(config.body);
}
// Set timeout
request.setHttpTimeout(config.timeout || this.timeout);
var response;
var attempt = 0;
var maxRetries = config.maxRetries || this.maxRetries;
do {
try {
response = request.execute();
// Log the request
this._logRequest(config, response, attempt);
// Check if we should retry
if (this._shouldRetry(response) && attempt < maxRetries) {
attempt++;
gs.sleep(1000 * attempt); // Exponential backoff
continue;
}
return {
statusCode: response.getStatusCode(),
body: response.getBody(),
headers: response.getHeaders(),
errorMessage: response.getErrorMessage(),
success: response.getStatusCode() >= 200 && response.getStatusCode() < 300
};
} catch (ex) {
gs.error('REST API Error on attempt ' + (attempt + 1) + ': ' + ex.getMessage());
if (attempt >= maxRetries) {
return {
statusCode: 0,
body: null,
headers: {},
errorMessage: ex.getMessage(),
success: false
};
}
attempt++;
gs.sleep(1000 * attempt);
}
} while (attempt <= maxRetries);
},
/**
* Determine if request should be retried based on status code
*/
_shouldRetry: function(response) {
var statusCode = response.getStatusCode();
return statusCode >= 500 || statusCode == 429 || statusCode == 408;
},
/**
* Log REST API requests for debugging and monitoring
*/
_logRequest: function(config, response, attempt) {
var logMessage = 'REST API Call - ' + config.method + ' ' + config.url +
' - Status: ' + response.getStatusCode() +
' - Attempt: ' + (attempt + 1);
if (response.getStatusCode() >= 400) {
gs.warn(logMessage + ' - Error: ' + response.getErrorMessage());
} else {
gs.info(logMessage);
}
},
type: 'RESTIntegrationHelper'
};
Configuration:
- Create new Script Include
- Name: RESTIntegrationHelper
- Client callable: false
- Active: true
- Application: Global
CMDB Data Quality Manager
NEW
Automated cleanup and validation utility for CMDB Configuration Items with comprehensive reporting
var CMDBDataQualityManager = Class.create();
CMDBDataQualityManager.prototype = {
initialize: function() {
this.processedCount = 0;
this.updatedCount = 0;
this.duplicatesFound = 0;
this.orphanedCount = 0;
this.maxRecordsPerRun = 1000; // Batch processing limit
this.dryRun = false; // Set to true for testing
},
/**
* Main cleanup method - orchestrates all cleanup operations
* @param {Object} options - Configuration options
* @param {boolean} options.dryRun - Preview mode without making changes
* @param {number} options.batchSize - Records to process per batch
* @param {Array} options.ciClasses - Specific CI classes to process
* @returns {Object} Cleanup summary report
*/
performCleanup: function(options) {
options = options || {};
this.dryRun = options.dryRun || false;
this.maxRecordsPerRun = options.batchSize || 1000;
var startTime = new GlideDateTime();
gs.info('CMDB Cleanup started at ' + startTime.getDisplayValue() +
(this.dryRun ? ' (DRY RUN MODE)' : ''));
var report = {
startTime: startTime.getDisplayValue(),
dryRun: this.dryRun,
operations: [],
summary: {}
};
try {
// 1. Clean up orphaned relationships
report.operations.push(this._cleanupOrphanedRelationships());
// 2. Remove duplicate CIs
report.operations.push(this._removeDuplicateCIs(options.ciClasses));
// 3. Update stale CI status
report.operations.push(this._updateStaleCIStatus());
// 4. Validate and fix missing mandatory fields
report.operations.push(this._validateMandatoryFields(options.ciClasses));
// 5. Clean up old discovery data
report.operations.push(this._cleanupOldDiscoveryData());
} catch (ex) {
gs.error('CMDB Cleanup error: ' + ex.getMessage());
report.error = ex.getMessage();
}
var endTime = new GlideDateTime();
report.endTime = endTime.getDisplayValue();
report.duration = gs.dateDiff(startTime.getNumericValue(), endTime.getNumericValue(), true);
// Generate summary
report.summary = this._generateSummary(report.operations);
// Log completion
gs.info('CMDB Cleanup completed. Duration: ' + report.duration +
'. Processed: ' + this.processedCount +
', Updated: ' + this.updatedCount);
return report;
},
/**
* Remove orphaned CI relationships
*/
_cleanupOrphanedRelationships: function() {
var operation = {
name: 'Orphaned Relationships Cleanup',
processed: 0,
removed: 0,
errors: []
};
try {
var gr = new GlideRecord('cmdb_rel_ci');
gr.addQuery('parent', '');
gr.addOrCondition('child', '');
gr.setLimit(this.maxRecordsPerRun);
gr.query();
while (gr.next()) {
operation.processed++;
if (!this.dryRun) {
gr.deleteRecord();
operation.removed++;
}
// Yield every 100 records to prevent timeout
if (operation.processed % 100 === 0) {
gs.sleep(100);
}
}
} catch (ex) {
operation.errors.push(ex.getMessage());
}
this.processedCount += operation.processed;
this.updatedCount += operation.removed;
return operation;
},
/**
* Identify and remove duplicate CIs based on name and class
*/
_removeDuplicateCIs: function(ciClasses) {
var operation = {
name: 'Duplicate CIs Removal',
processed: 0,
duplicates: 0,
errors: []
};
try {
var classesToProcess = ciClasses || ['cmdb_ci_server', 'cmdb_ci_computer', 'cmdb_ci_service'];
for (var i = 0; i < classesToProcess.length; i++) {
var className = classesToProcess[i];
// Find duplicates using aggregate query
var ga = new GlideAggregate(className);
ga.addAggregate('COUNT', 'name');
ga.addHaving('COUNT', '>', '1');
ga.addNotNullQuery('name');
ga.groupBy('name');
ga.query();
while (ga.next()) {
var duplicateName = ga.getValue('name');
this._processDuplicates(className, duplicateName, operation);
if (operation.processed >= this.maxRecordsPerRun) {
break;
}
}
}
} catch (ex) {
operation.errors.push(ex.getMessage());
}
return operation;
},
/**
* Process duplicate CIs for a given name
*/
_processDuplicates: function(className, name, operation) {
var gr = new GlideRecord(className);
gr.addQuery('name', name);
gr.orderByDesc('sys_updated_on'); // Keep the most recently updated
gr.query();
var keepFirst = true;
while (gr.next()) {
operation.processed++;
if (keepFirst) {
keepFirst = false;
continue; // Keep the first (most recent) record
}
// This is a duplicate - remove it
if (!this.dryRun) {
gr.deleteRecord();
operation.duplicates++;
}
}
},
/**
* Update CIs with stale status based on last discovery
*/
_updateStaleCIStatus: function() {
var operation = {
name: 'Stale CI Status Update',
processed: 0,
updated: 0,
errors: []
};
try {
// Find CIs not updated by discovery in 90 days
var staleDate = new GlideDateTime();
staleDate.addDaysLocalTime(-90);
var gr = new GlideRecord('cmdb_ci');
gr.addQuery('last_discovered', '<', staleDate);
gr.addQuery('install_status', '1'); // Installed
gr.setLimit(this.maxRecordsPerRun);
gr.query();
while (gr.next()) {
operation.processed++;
if (!this.dryRun) {
gr.setValue('install_status', '7'); // Retired
gr.setValue('comments', 'Auto-retired due to stale discovery data');
gr.update();
operation.updated++;
}
if (operation.processed % 50 === 0) {
gs.sleep(100);
}
}
} catch (ex) {
operation.errors.push(ex.getMessage());
}
return operation;
},
/**
* Validate and fix missing mandatory fields
*/
_validateMandatoryFields: function(ciClasses) {
var operation = {
name: 'Mandatory Fields Validation',
processed: 0,
updated: 0,
errors: []
};
try {
var classesToProcess = ciClasses || ['cmdb_ci_server'];
for (var i = 0; i < classesToProcess.length; i++) {
var className = classesToProcess[i];
var gr = new GlideRecord(className);
gr.addQuery('name', '');
gr.addOrCondition('operational_status', '');
gr.setLimit(this.maxRecordsPerRun / classesToProcess.length);
gr.query();
while (gr.next()) {
operation.processed++;
var updated = false;
if (!this.dryRun) {
// Fix empty name
if (gr.getValue('name') == '') {
gr.setValue('name', 'Unknown-' + gr.getUniqueValue().substring(0, 8));
updated = true;
}
// Fix empty operational status
if (gr.getValue('operational_status') == '') {
gr.setValue('operational_status', '1'); // Operational
updated = true;
}
if (updated) {
gr.update();
operation.updated++;
}
}
}
}
} catch (ex) {
operation.errors.push(ex.getMessage());
}
return operation;
},
/**
* Clean up old discovery data and logs
*/
_cleanupOldDiscoveryData: function() {
var operation = {
name: 'Old Discovery Data Cleanup',
processed: 0,
removed: 0,
errors: []
};
try {
// Remove discovery logs older than 6 months
var cutoffDate = new GlideDateTime();
cutoffDate.addMonthsLocalTime(-6);
var gr = new GlideRecord('discovery_log');
gr.addQuery('sys_created_on', '<', cutoffDate);
gr.setLimit(this.maxRecordsPerRun);
gr.query();
while (gr.next()) {
operation.processed++;
if (!this.dryRun) {
gr.deleteRecord();
operation.removed++;
}
if (operation.processed % 100 === 0) {
gs.sleep(100);
}
}
} catch (ex) {
operation.errors.push(ex.getMessage());
}
return operation;
},
/**
* Generate cleanup summary report
*/
_generateSummary: function(operations) {
var summary = {
totalProcessed: 0,
totalUpdated: 0,
operationCount: operations.length,
hasErrors: false
};
for (var i = 0; i < operations.length; i++) {
var op = operations[i];
summary.totalProcessed += op.processed || 0;
summary.totalUpdated += (op.removed || 0) + (op.updated || 0) + (op.duplicates || 0);
if (op.errors && op.errors.length > 0) {
summary.hasErrors = true;
}
}
return summary;
},
type: 'CMDBDataQualityManager'
};
Configuration:
- Create new Script Include
- Name: RESTIntegrationHelper
- Client callable: false
- Active: true
- Application: Global
UI Actions (1)
Bulk Update Assignment Group
Allow bulk updating of assignment group for selected records in list view
// Client-side script
if (confirm('Update assignment group for selected records?')) {
var selectedRows = g_list.getChecked();
if (selectedRows.length == 0) {
alert('Please select at least one record.');
return;
}
var newGroup = prompt('Enter new assignment group name:');
if (newGroup) {
gsftSubmit(null, g_list.getFixedQuery(), 'bulk_update_assignment',
'sysparm_assignment_group=' + encodeURIComponent(newGroup) +
'&sysparm_checked_items=' + selectedRows);
}
}
Configuration:
- Table: incident
- Action name: bulk_update_assignment
- Form button: false
- List button: true
- Show insert: false
- Show update: false