Script Includes
Data Validation Utilities
Reusable functions for validating data and enforcing business rules.
var ValidationUtils = Class.create();
ValidationUtils.prototype = {
/**
* Validate required fields are populated
*
* @param {GlideRecord} record - Record to validate
* @param {Array} fieldNames - Array of required field names
* @returns {object} {valid: boolean, errors: Array}
*/
validateRequiredFields: function(record, fieldNames) {
var result = {
valid: true,
errors: []
};
if (!record || !fieldNames) {
result.valid = false;
result.errors.push('Invalid parameters for validation');
return result;
}
fieldNames.forEach(function(fieldName) {
var value = record.getValue(fieldName);
if (!value || value === '' || value === 'NULL') {
result.valid = false;
var fieldLabel = record.getElement(fieldName).getLabel();
result.errors.push(fieldLabel + ' is required');
}
});
return result;
},
/**
* Validate field length constraints
*
* @param {GlideRecord} record - Record to validate
* @param {object} fieldLimits - Object with fieldName: {min, max}
* @returns {object} {valid: boolean, errors: Array}
*/
validateFieldLengths: function(record, fieldLimits) {
var result = {
valid: true,
errors: []
};
for (var fieldName in fieldLimits) {
var limits = fieldLimits[fieldName];
var value = record.getValue(fieldName);
var fieldLabel = record.getElement(fieldName).getLabel();
if (value) {
var length = value.toString().length;
if (limits.min && length < limits.min) {
result.valid = false;
result.errors.push(fieldLabel + ' must be at least ' + limits.min + ' characters');
}
if (limits.max && length > limits.max) {
result.valid = false;
result.errors.push(fieldLabel + ' cannot exceed ' + limits.max + ' characters');
}
}
}
return result;
},
/**
* Validate date ranges
*
* @param {GlideDateTime} startDate - Start date
* @param {GlideDateTime} endDate - End date
* @param {object} options - {allowSameDay, minDays, maxDays}
* @returns {object} {valid: boolean, errors: Array}
*/
validateDateRange: function(startDate, endDate, options) {
var result = {
valid: true,
errors: []
};
options = options || {};
if (!startDate || !endDate) {
result.valid = false;
result.errors.push('Both start and end dates are required');
return result;
}
var start = new GlideDateTime(startDate);
var end = new GlideDateTime(endDate);
// Check end is after start
if (end.before(start)) {
result.valid = false;
result.errors.push('End date must be after start date');
}
// Check same day
if (!options.allowSameDay && end.equals(start)) {
result.valid = false;
result.errors.push('Start and end date cannot be the same');
}
// Check minimum days
if (options.minDays) {
var daysDiff = gs.dateDiff(start.getValue(), end.getValue(), true) / (24 * 60 * 60);
if (daysDiff < options.minDays) {
result.valid = false;
result.errors.push('Date range must be at least ' + options.minDays + ' days');
}
}
// Check maximum days
if (options.maxDays) {
var daysDiff = gs.dateDiff(start.getValue(), end.getValue(), true) / (24 * 60 * 60);
if (daysDiff > options.maxDays) {
result.valid = false;
result.errors.push('Date range cannot exceed ' + options.maxDays + ' days');
}
}
return result;
},
/**
* Validate numeric range
*
* @param {number} value - Value to validate
* @param {number} min - Minimum value (inclusive)
* @param {number} max - Maximum value (inclusive)
* @param {string} fieldLabel - Field label for error message
* @returns {object} {valid: boolean, errors: Array}
*/
validateNumericRange: function(value, min, max, fieldLabel) {
var result = {
valid: true,
errors: []
};
fieldLabel = fieldLabel || 'Value';
var numValue = parseFloat(value);
if (isNaN(numValue)) {
result.valid = false;
result.errors.push(fieldLabel + ' must be a valid number');
return result;
}
if (min !== null && min !== undefined && numValue < min) {
result.valid = false;
result.errors.push(fieldLabel + ' must be at least ' + min);
}
if (max !== null && max !== undefined && numValue > max) {
result.valid = false;
result.errors.push(fieldLabel + ' cannot exceed ' + max);
}
return result;
},
/**
* Validate business rule: record must be in specific state for action
*
* @param {GlideRecord} record - Record to validate
* @param {string} stateField - Name of state field
* @param {Array} allowedStates - Array of allowed state values
* @param {string} actionName - Name of action for error message
* @returns {object} {valid: boolean, errors: Array}
*/
validateStateTransition: function(record, stateField, allowedStates, actionName) {
var result = {
valid: true,
errors: []
};
if (!record || !stateField || !allowedStates) {
result.valid = false;
result.errors.push('Invalid parameters for state validation');
return result;
}
var currentState = record.getValue(stateField);
actionName = actionName || 'this action';
if (allowedStates.indexOf(currentState) === -1) {
result.valid = false;
var stateLabel = record.getElement(stateField).getLabel();
result.errors.push('Cannot perform ' + actionName + ' in current ' + stateLabel);
}
return result;
},
/**
* Validate unique field value (no duplicates in table)
*
* @param {GlideRecord} record - Record to validate
* @param {string} fieldName - Field name to check
* @param {string} scope - (Optional) Additional query to scope the uniqueness check
* @returns {object} {valid: boolean, errors: Array}
*/
validateUnique: function(record, fieldName, scope) {
var result = {
valid: true,
errors: []
};
var value = record.getValue(fieldName);
if (!value) {
return result; // Empty values don't need uniqueness check
}
var gr = new GlideRecord(record.getTableName());
gr.addQuery(fieldName, value);
// Exclude current record if updating
if (record.sys_id) {
gr.addQuery('sys_id', '!=', record.sys_id);
}
// Apply additional scope if provided
if (scope) {
gr.addEncodedQuery(scope);
}
gr.setLimit(1);
gr.query();
if (gr.hasNext()) {
result.valid = false;
var fieldLabel = record.getElement(fieldName).getLabel();
result.errors.push(fieldLabel + ' must be unique. This value already exists.');
}
return result;
},
/**
* Validate user permissions for action
*
* @param {string} userId - User sys_id
* @param {Array} requiredRoles - Array of role names (user needs at least one)
* @param {string} actionName - Name of action for error message
* @returns {object} {valid: boolean, errors: Array}
*/
validateUserPermission: function(userId, requiredRoles, actionName) {
var result = {
valid: true,
errors: []
};
userId = userId || gs.getUserID();
actionName = actionName || 'perform this action';
if (!requiredRoles || requiredRoles.length === 0) {
return result;
}
var hasRole = false;
var grUser = new GlideRecord('sys_user');
if (grUser.get(userId)) {
for (var i = 0; i < requiredRoles.length; i++) {
if (grUser.hasRole(requiredRoles[i])) {
hasRole = true;
break;
}
}
}
if (!hasRole) {
result.valid = false;
result.errors.push('You do not have permission to ' + actionName);
}
return result;
},
/**
* Comprehensive validation - combines multiple validation types
*
* @param {GlideRecord} record - Record to validate
* @param {object} rules - Validation rules configuration
* @returns {object} {valid: boolean, errors: Array}
*/
validate: function(record, rules) {
var allResults = {
valid: true,
errors: []
};
// Required fields validation
if (rules.required) {
var reqResult = this.validateRequiredFields(record, rules.required);
if (!reqResult.valid) {
allResults.valid = false;
allResults.errors = allResults.errors.concat(reqResult.errors);
}
}
// Field length validation
if (rules.lengths) {
var lengthResult = this.validateFieldLengths(record, rules.lengths);
if (!lengthResult.valid) {
allResults.valid = false;
allResults.errors = allResults.errors.concat(lengthResult.errors);
}
}
// Unique field validation
if (rules.unique) {
for (var i = 0; i < rules.unique.length; i++) {
var uniqueResult = this.validateUnique(record, rules.unique[i]);
if (!uniqueResult.valid) {
allResults.valid = false;
allResults.errors = allResults.errors.concat(uniqueResult.errors);
}
}
}
// Custom validation functions
if (rules.custom && typeof rules.custom === 'function') {
var customResult = rules.custom(record);
if (customResult && !customResult.valid) {
allResults.valid = false;
allResults.errors = allResults.errors.concat(customResult.errors);
}
}
return allResults;
},
type: 'ValidationUtils'
};How to use it
1. Create a new Script Include 2. Set Name to "ValidationUtils" 3. Leave "Client callable" unchecked 4. Copy the code above 5. Use in Business Rules for comprehensive validation
Adapt the table names, fields, and conditions to your instance. Test the behavior in a development environment before using it in production.