Data Validation Utilities
Reusable functions for validating data and enforcing business rules.
#validation #data-quality #business-rules #utilities #required-fields #date-range
Script Code
JavaScript
1var ValidationUtils = Class.create();
2ValidationUtils.prototype = {
3
4 /**
5 * Validate required fields are populated
6 *
7 * @param {GlideRecord} record - Record to validate
8 * @param {Array} fieldNames - Array of required field names
9 * @returns {object} {valid: boolean, errors: Array}
10 */
11 validateRequiredFields: function(record, fieldNames) {
12 var result = {
13 valid: true,
14 errors: []
15 };
16
17 if (!record || !fieldNames) {
18 result.valid = false;
19 result.errors.push('Invalid parameters for validation');
20 return result;
21 }
22
23 fieldNames.forEach(function(fieldName) {
24 var value = record.getValue(fieldName);
25
26 if (!value || value === '' || value === 'NULL') {
27 result.valid = false;
28 var fieldLabel = record.getElement(fieldName).getLabel();
29 result.errors.push(fieldLabel + ' is required');
30 }
31 });
32
33 return result;
34 },
35
36 /**
37 * Validate field length constraints
38 *
39 * @param {GlideRecord} record - Record to validate
40 * @param {object} fieldLimits - Object with fieldName: {min, max}
41 * @returns {object} {valid: boolean, errors: Array}
42 */
43 validateFieldLengths: function(record, fieldLimits) {
44 var result = {
45 valid: true,
46 errors: []
47 };
48
49 for (var fieldName in fieldLimits) {
50 var limits = fieldLimits[fieldName];
51 var value = record.getValue(fieldName);
52 var fieldLabel = record.getElement(fieldName).getLabel();
53
54 if (value) {
55 var length = value.toString().length;
56
57 if (limits.min && length < limits.min) {
58 result.valid = false;
59 result.errors.push(fieldLabel + ' must be at least ' + limits.min + ' characters');
60 }
61
62 if (limits.max && length > limits.max) {
63 result.valid = false;
64 result.errors.push(fieldLabel + ' cannot exceed ' + limits.max + ' characters');
65 }
66 }
67 }
68
69 return result;
70 },
71
72 /**
73 * Validate date ranges
74 *
75 * @param {GlideDateTime} startDate - Start date
76 * @param {GlideDateTime} endDate - End date
77 * @param {object} options - {allowSameDay, minDays, maxDays}
78 * @returns {object} {valid: boolean, errors: Array}
79 */
80 validateDateRange: function(startDate, endDate, options) {
81 var result = {
82 valid: true,
83 errors: []
84 };
85
86 options = options || {};
87
88 if (!startDate || !endDate) {
89 result.valid = false;
90 result.errors.push('Both start and end dates are required');
91 return result;
92 }
93
94 var start = new GlideDateTime(startDate);
95 var end = new GlideDateTime(endDate);
96
97 // Check end is after start
98 if (end.before(start)) {
99 result.valid = false;
100 result.errors.push('End date must be after start date');
101 }
102
103 // Check same day
104 if (!options.allowSameDay && end.equals(start)) {
105 result.valid = false;
106 result.errors.push('Start and end date cannot be the same');
107 }
108
109 // Check minimum days
110 if (options.minDays) {
111 var daysDiff = gs.dateDiff(start.getValue(), end.getValue(), true) / (24 * 60 * 60);
112 if (daysDiff < options.minDays) {
113 result.valid = false;
114 result.errors.push('Date range must be at least ' + options.minDays + ' days');
115 }
116 }
117
118 // Check maximum days
119 if (options.maxDays) {
120 var daysDiff = gs.dateDiff(start.getValue(), end.getValue(), true) / (24 * 60 * 60);
121 if (daysDiff > options.maxDays) {
122 result.valid = false;
123 result.errors.push('Date range cannot exceed ' + options.maxDays + ' days');
124 }
125 }
126
127 return result;
128 },
129
130 /**
131 * Validate numeric range
132 *
133 * @param {number} value - Value to validate
134 * @param {number} min - Minimum value (inclusive)
135 * @param {number} max - Maximum value (inclusive)
136 * @param {string} fieldLabel - Field label for error message
137 * @returns {object} {valid: boolean, errors: Array}
138 */
139 validateNumericRange: function(value, min, max, fieldLabel) {
140 var result = {
141 valid: true,
142 errors: []
143 };
144
145 fieldLabel = fieldLabel || 'Value';
146 var numValue = parseFloat(value);
147
148 if (isNaN(numValue)) {
149 result.valid = false;
150 result.errors.push(fieldLabel + ' must be a valid number');
151 return result;
152 }
153
154 if (min !== null && min !== undefined && numValue < min) {
155 result.valid = false;
156 result.errors.push(fieldLabel + ' must be at least ' + min);
157 }
158
159 if (max !== null && max !== undefined && numValue > max) {
160 result.valid = false;
161 result.errors.push(fieldLabel + ' cannot exceed ' + max);
162 }
163
164 return result;
165 },
166
167 /**
168 * Validate business rule: record must be in specific state for action
169 *
170 * @param {GlideRecord} record - Record to validate
171 * @param {string} stateField - Name of state field
172 * @param {Array} allowedStates - Array of allowed state values
173 * @param {string} actionName - Name of action for error message
174 * @returns {object} {valid: boolean, errors: Array}
175 */
176 validateStateTransition: function(record, stateField, allowedStates, actionName) {
177 var result = {
178 valid: true,
179 errors: []
180 };
181
182 if (!record || !stateField || !allowedStates) {
183 result.valid = false;
184 result.errors.push('Invalid parameters for state validation');
185 return result;
186 }
187
188 var currentState = record.getValue(stateField);
189 actionName = actionName || 'this action';
190
191 if (allowedStates.indexOf(currentState) === -1) {
192 result.valid = false;
193 var stateLabel = record.getElement(stateField).getLabel();
194 result.errors.push('Cannot perform ' + actionName + ' in current ' + stateLabel);
195 }
196
197 return result;
198 },
199
200 /**
201 * Validate unique field value (no duplicates in table)
202 *
203 * @param {GlideRecord} record - Record to validate
204 * @param {string} fieldName - Field name to check
205 * @param {string} scope - (Optional) Additional query to scope the uniqueness check
206 * @returns {object} {valid: boolean, errors: Array}
207 */
208 validateUnique: function(record, fieldName, scope) {
209 var result = {
210 valid: true,
211 errors: []
212 };
213
214 var value = record.getValue(fieldName);
215 if (!value) {
216 return result; // Empty values don't need uniqueness check
217 }
218
219 var gr = new GlideRecord(record.getTableName());
220 gr.addQuery(fieldName, value);
221
222 // Exclude current record if updating
223 if (record.sys_id) {
224 gr.addQuery('sys_id', '!=', record.sys_id);
225 }
226
227 // Apply additional scope if provided
228 if (scope) {
229 gr.addEncodedQuery(scope);
230 }
231
232 gr.setLimit(1);
233 gr.query();
234
235 if (gr.hasNext()) {
236 result.valid = false;
237 var fieldLabel = record.getElement(fieldName).getLabel();
238 result.errors.push(fieldLabel + ' must be unique. This value already exists.');
239 }
240
241 return result;
242 },
243
244 /**
245 * Validate user permissions for action
246 *
247 * @param {string} userId - User sys_id
248 * @param {Array} requiredRoles - Array of role names (user needs at least one)
249 * @param {string} actionName - Name of action for error message
250 * @returns {object} {valid: boolean, errors: Array}
251 */
252 validateUserPermission: function(userId, requiredRoles, actionName) {
253 var result = {
254 valid: true,
255 errors: []
256 };
257
258 userId = userId || gs.getUserID();
259 actionName = actionName || 'perform this action';
260
261 if (!requiredRoles || requiredRoles.length === 0) {
262 return result;
263 }
264
265 var hasRole = false;
266 var grUser = new GlideRecord('sys_user');
267
268 if (grUser.get(userId)) {
269 for (var i = 0; i < requiredRoles.length; i++) {
270 if (grUser.hasRole(requiredRoles[i])) {
271 hasRole = true;
272 break;
273 }
274 }
275 }
276
277 if (!hasRole) {
278 result.valid = false;
279 result.errors.push('You do not have permission to ' + actionName);
280 }
281
282 return result;
283 },
284
285 /**
286 * Comprehensive validation - combines multiple validation types
287 *
288 * @param {GlideRecord} record - Record to validate
289 * @param {object} rules - Validation rules configuration
290 * @returns {object} {valid: boolean, errors: Array}
291 */
292 validate: function(record, rules) {
293 var allResults = {
294 valid: true,
295 errors: []
296 };
297
298 // Required fields validation
299 if (rules.required) {
300 var reqResult = this.validateRequiredFields(record, rules.required);
301 if (!reqResult.valid) {
302 allResults.valid = false;
303 allResults.errors = allResults.errors.concat(reqResult.errors);
304 }
305 }
306
307 // Field length validation
308 if (rules.lengths) {
309 var lengthResult = this.validateFieldLengths(record, rules.lengths);
310 if (!lengthResult.valid) {
311 allResults.valid = false;
312 allResults.errors = allResults.errors.concat(lengthResult.errors);
313 }
314 }
315
316 // Unique field validation
317 if (rules.unique) {
318 for (var i = 0; i < rules.unique.length; i++) {
319 var uniqueResult = this.validateUnique(record, rules.unique[i]);
320 if (!uniqueResult.valid) {
321 allResults.valid = false;
322 allResults.errors = allResults.errors.concat(uniqueResult.errors);
323 }
324 }
325 }
326
327 // Custom validation functions
328 if (rules.custom && typeof rules.custom === 'function') {
329 var customResult = rules.custom(record);
330 if (customResult && !customResult.valid) {
331 allResults.valid = false;
332 allResults.errors = allResults.errors.concat(customResult.errors);
333 }
334 }
335
336 return allResults;
337 },
338
339 type: 'ValidationUtils'
340};
How to Use
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