formio/node_modules/json-schema/lib/validate.js   F
last analyzed

Complexity

Total Complexity 114
Complexity/F 8.77

Size

Lines of Code 258
Function Count 13

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
wmc 114
nc 1772093440
mnd 6
bc 67
fnc 13
dl 0
loc 258
rs 1.5789
bpm 5.1538
cpm 8.7692
noi 5
c 0
b 0
f 0

2 Functions

Rating   Name   Duplication   Size   Complexity  
A validate.js ➔ define 0 3 1
B validate.js ➔ ?!? 0 243 1

How to fix   Complexity   

Complexity

Complex classes like formio/node_modules/json-schema/lib/validate.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * JSONSchema Validator - Validates JavaScript objects using JSON Schemas
3
 *	(http://www.json.com/json-schema-proposal/)
4
 *
5
 * Copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com)
6
 * Licensed under the MIT (MIT-LICENSE.txt) license.
7
To use the validator call the validate function with an instance object and an optional schema object.
8
If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
9
that schema will be used to validate and the schema parameter is not necessary (if both exist,
10
both validations will occur).
11
The validate method will return an array of validation errors. If there are no errors, then an
12
empty list will be returned. A validation error will have two properties:
13
"property" which indicates which property had the error
14
"message" which indicates what the error was
15
 */
16
(function (root, factory) {
17
    if (typeof define === 'function' && define.amd) {
18
        // AMD. Register as an anonymous module.
19
        define([], function () {
20
            return factory();
21
        });
22
    } else if (typeof module === 'object' && module.exports) {
23
        // Node. Does not work with strict CommonJS, but
24
        // only CommonJS-like environments that support module.exports,
25
        // like Node.
26
        module.exports = factory();
27
    } else {
28
        // Browser globals
29
        root.jsonSchema = factory();
30
    }
31
}(this, function () {// setup primitive classes to be JSON Schema types
32
var exports = validate
33
exports.Integer = {type:"integer"};
34
var primitiveConstructors = {
35
	String: String,
36
	Boolean: Boolean,
37
	Number: Number,
38
	Object: Object,
39
	Array: Array,
40
	Date: Date
41
}
42
exports.validate = validate;
43
function validate(/*Any*/instance,/*Object*/schema) {
44
		// Summary:
45
		//  	To use the validator call JSONSchema.validate with an instance object and an optional schema object.
46
		// 		If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
47
		// 		that schema will be used to validate and the schema parameter is not necessary (if both exist,
48
		// 		both validations will occur).
49
		// 		The validate method will return an object with two properties:
50
		// 			valid: A boolean indicating if the instance is valid by the schema
51
		// 			errors: An array of validation errors. If there are no errors, then an
52
		// 					empty list will be returned. A validation error will have two properties:
53
		// 						property: which indicates which property had the error
54
		// 						message: which indicates what the error was
55
		//
56
		return validate(instance, schema, {changing: false});//, coerce: false, existingOnly: false});
0 ignored issues
show
Bug introduced by
The call to validate seems to have too many arguments starting with {IdentifierNode(changing...leanLiteralNode(false)}.
Loading history...
57
	};
58
exports.checkPropertyChange = function(/*Any*/value,/*Object*/schema, /*String*/property) {
59
		// Summary:
60
		// 		The checkPropertyChange method will check to see if an value can legally be in property with the given schema
61
		// 		This is slightly different than the validate method in that it will fail if the schema is readonly and it will
62
		// 		not check for self-validation, it is assumed that the passed in value is already internally valid.
63
		// 		The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for
64
		// 		information.
65
		//
66
		return validate(value, schema, {changing: property || "property"});
0 ignored issues
show
Bug introduced by
The call to validate seems to have too many arguments starting with {IdentifierNode(changing...LiteralNode(property))}.
Loading history...
67
	};
68
var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*Object*/options) {
0 ignored issues
show
Comprehensibility introduced by
It seems like you are trying to overwrite a function name here. validate is already defined in line 43 as a function. While this will work, it can be very confusing.
Loading history...
69
70
	if (!options) options = {};
71
	var _changing = options.changing;
72
73
	function getType(schema){
74
		return schema.type || (primitiveConstructors[schema.name] == schema && schema.name.toLowerCase());
75
	}
76
	var errors = [];
77
	// validate a value against a property definition
78
	function checkProp(value, schema, path,i){
79
80
		var l;
81
		path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i;
82
		function addError(message){
83
			errors.push({property:path,message:message});
84
		}
85
86
		if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function') && !(schema && getType(schema))){
87
			if(typeof schema == 'function'){
88
				if(!(value instanceof schema)){
89
					addError("is not an instance of the class/constructor " + schema.name);
90
				}
91
			}else if(schema){
92
				addError("Invalid schema/property definition " + schema);
93
			}
94
			return null;
95
		}
96
		if(_changing && schema.readonly){
97
			addError("is a readonly field, it can not be changed");
98
		}
99
		if(schema['extends']){ // if it extends another schema, it must pass that schema as well
100
			checkProp(value,schema['extends'],path,i);
101
		}
102
		// validate a value against a type definition
103
		function checkType(type,value){
104
			if(type){
105
				if(typeof type == 'string' && type != 'any' &&
106
						(type == 'null' ? value !== null : typeof value != type) &&
107
						!(value instanceof Array && type == 'array') &&
108
						!(value instanceof Date && type == 'date') &&
109
						!(type == 'integer' && value%1===0)){
110
					return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}];
111
				}
112
				if(type instanceof Array){
113
					var unionErrors=[];
114
					for(var j = 0; j < type.length; j++){ // a union type
115
						if(!(unionErrors=checkType(type[j],value)).length){
116
							break;
117
						}
118
					}
119
					if(unionErrors.length){
120
						return unionErrors;
121
					}
122
				}else if(typeof type == 'object'){
123
					var priorErrors = errors;
124
					errors = [];
125
					checkProp(value,type,path);
126
					var theseErrors = errors;
127
					errors = priorErrors;
128
					return theseErrors;
129
				}
130
			}
131
			return [];
132
		}
133
		if(value === undefined){
134
			if(schema.required){
135
				addError("is missing and it is required");
136
			}
137
		}else{
138
			errors = errors.concat(checkType(getType(schema),value));
139
			if(schema.disallow && !checkType(schema.disallow,value).length){
140
				addError(" disallowed value was matched");
141
			}
142
			if(value !== null){
143
				if(value instanceof Array){
144
					if(schema.items){
145
						var itemsIsArray = schema.items instanceof Array;
146
						var propDef = schema.items;
147
						for (i = 0, l = value.length; i < l; i += 1) {
148
							if (itemsIsArray)
149
								propDef = schema.items[i];
150
							if (options.coerce)
151
								value[i] = options.coerce(value[i], propDef);
152
							errors.concat(checkProp(value[i],propDef,path,i));
153
						}
154
					}
155
					if(schema.minItems && value.length < schema.minItems){
156
						addError("There must be a minimum of " + schema.minItems + " in the array");
157
					}
158
					if(schema.maxItems && value.length > schema.maxItems){
159
						addError("There must be a maximum of " + schema.maxItems + " in the array");
160
					}
161
				}else if(schema.properties || schema.additionalProperties){
162
					errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties));
163
				}
164
				if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){
165
					addError("does not match the regex pattern " + schema.pattern);
166
				}
167
				if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){
168
					addError("may only be " + schema.maxLength + " characters long");
169
				}
170
				if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){
171
					addError("must be at least " + schema.minLength + " characters long");
172
				}
173
				if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum &&
174
						schema.minimum > value){
175
					addError("must have a minimum value of " + schema.minimum);
176
				}
177
				if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum &&
178
						schema.maximum < value){
179
					addError("must have a maximum value of " + schema.maximum);
180
				}
181
				if(schema['enum']){
182
					var enumer = schema['enum'];
183
					l = enumer.length;
184
					var found;
185
					for(var j = 0; j < l; j++){
186
						if(enumer[j]===value){
187
							found=1;
188
							break;
189
						}
190
					}
191
					if(!found){
192
						addError("does not have a value in the enumeration " + enumer.join(", "));
193
					}
194
				}
195
				if(typeof schema.maxDecimal == 'number' &&
196
					(value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){
197
					addError("may only have " + schema.maxDecimal + " digits of decimal places");
198
				}
199
			}
200
		}
201
		return null;
202
	}
203
	// validate an object against a schema
204
	function checkObj(instance,objTypeDef,path,additionalProp){
205
206
		if(typeof objTypeDef =='object'){
207
			if(typeof instance != 'object' || instance instanceof Array){
208
				errors.push({property:path,message:"an object is required"});
209
			}
210
			
211
			for(var i in objTypeDef){ 
212
				if(objTypeDef.hasOwnProperty(i)){
213
					var value = instance[i];
214
					// skip _not_ specified properties
215
					if (value === undefined && options.existingOnly) continue;
216
					var propDef = objTypeDef[i];
217
					// set default
218
					if(value === undefined && propDef["default"]){
219
						value = instance[i] = propDef["default"];
220
					}
221
					if(options.coerce && i in instance){
222
						value = instance[i] = options.coerce(value, propDef);
223
					}
224
					checkProp(value,propDef,path,i);
225
				}
226
			}
227
		}
228
		for(i in instance){
229
			if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){
230
				if (options.filter) {
231
					delete instance[i];
232
					continue;
233
				} else {
234
					errors.push({property:path,message:(typeof value) + "The property " + i +
0 ignored issues
show
Bug introduced by
The variable value seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable errors is changed as part of the for-each loop for example by errors.concat(checkProp(...alue.$schema, path, i)) on line 250. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
235
						" is not defined in the schema and the schema does not allow additional properties"});
236
				}
237
			}
238
			var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires;
239
			if(requires && !(requires in instance)){
240
				errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"});
241
			}
242
			value = instance[i];
243
			if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){
244
				if(options.coerce){
245
					value = instance[i] = options.coerce(value, additionalProp);
246
				}
247
				checkProp(value,additionalProp,path,i);
248
			}
249
			if(!_changing && value && value.$schema){
250
				errors = errors.concat(checkProp(value,value.$schema,path,i));
251
			}
252
		}
253
		return errors;
254
	}
255
	if(schema){
256
		checkProp(instance,schema,'',_changing || '');
257
	}
258
	if(!_changing && instance && instance.$schema){
259
		checkProp(instance,instance.$schema,'','');
260
	}
261
	return {valid:!errors.length,errors:errors};
262
};
263
exports.mustBeValid = function(result){
264
	//	summary:
265
	//		This checks to ensure that the result is valid and will throw an appropriate error message if it is not
266
	// result: the result returned from checkPropertyChange or validate
267
	if(!result.valid){
268
		throw new TypeError(result.errors.map(function(error){return "for property " + error.property + ': ' + error.message;}).join(", \n"));
269
	}
270
}
271
272
return exports;
273
}));
274