Skip to Content

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

Explore More Scripts

Browse our complete library of ServiceNow scripts

View All Scripts