simpl-schema
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -15,4 +15,2 @@ 'use strict'; | ||
var _utility = require('./utility.js'); | ||
var _underscore = require('underscore'); | ||
@@ -22,4 +20,18 @@ | ||
var _utility = require('./utility.js'); | ||
var _SimpleSchema = require('./SimpleSchema'); | ||
var _convertToProperType = require('./clean/convertToProperType'); | ||
var _convertToProperType2 = _interopRequireDefault(_convertToProperType); | ||
var _setAutoValues = require('./clean/setAutoValues'); | ||
var _setAutoValues2 = _interopRequireDefault(_setAutoValues); | ||
var _clone = require('clone'); | ||
var _clone2 = _interopRequireDefault(_clone); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -31,2 +43,3 @@ | ||
* @param {Object} [options] | ||
* @param {Boolean} [options.mutate=false] - Mutate doc. Set this to true to improve performance if you don't mind mutating the object you're cleaning. | ||
* @param {Boolean} [options.filter=true] - Do filtering? | ||
@@ -39,2 +52,3 @@ * @param {Boolean} [options.autoConvert=true] - Do automatic type converting? | ||
* @param {Boolean} [options.isModifier=false] - Is doc a modifier object? | ||
* @param {Boolean} [options.mongoObject] - If you already have the mongoObject instance, pass it to improve performance | ||
* @param {Object} [options.extendAutoValueContext] - This object will be added to the `this` context of autoValue functions. | ||
@@ -55,4 +69,7 @@ * @returns {Object} The modified doc. | ||
var mDoc = new _mongoObject2.default(doc, ss._blackboxKeys); | ||
// Clone so we do not mutate | ||
var cleanDoc = options.mutate ? doc : (0, _clone2.default)(doc); | ||
var mongoObject = options.mongoObject || new _mongoObject2.default(cleanDoc, ss.blackboxKeys()); | ||
// Clean loop | ||
@@ -63,56 +80,65 @@ if (options.filter || options.autoConvert || options.removeEmptyStrings || options.trimStrings) { | ||
mDoc.forEachNode(function eachNode() { | ||
mongoObject.forEachNode(function eachNode() { | ||
// The value of a $unset is irrelevant, so no point in cleaning it. | ||
// Also we do not care if fields not in the schema are unset. | ||
if (this.operator === '$unset') return; | ||
var gKey = this.genericKey; | ||
if (!gKey) return; | ||
var val = this.value; | ||
if (val === undefined) return; | ||
var p = void 0; | ||
var def = void 0; | ||
var val = void 0; | ||
function trimAndRemoveStrings() { | ||
// trim strings | ||
if (options.trimStrings && typeof val === 'string' && (!def || def && def.trim !== false)) { | ||
val = val.trim(); | ||
this.updateValue(val); | ||
} | ||
// remove empty strings | ||
if (options.removeEmptyStrings && (!this.operator || this.operator === '$set') && typeof val === 'string' && !val.length) { | ||
// For a document, we remove any fields that are being set to an empty string | ||
// Filter out props if necessary | ||
if (options.filter && !ss.allowsKey(gKey) || options.removeNullsFromArrays && this.isArrayItem) { | ||
// XXX Special handling for $each; maybe this could be made nicer | ||
if (this.position.slice(-7) === '[$each]') { | ||
mongoObject.removeValueForPosition(this.position.slice(0, -7)); | ||
removedPositions.push(this.position.slice(0, -7)); | ||
} else { | ||
this.remove(); | ||
// For a modifier, we $unset any fields that are being set to an empty string. But only if we're not already within an entire object that is being set. | ||
if (this.operator === '$set' && this.position.match(/\[.+?\]/g).length < 2) { | ||
p = this.position.replace('$set', '$unset'); | ||
mDoc.setValueForPosition(p, ''); | ||
} | ||
removedPositions.push(this.position); | ||
} | ||
if (_SimpleSchema.SimpleSchema.debug) { | ||
console.info('SimpleSchema.clean: filtered out value that would have affected key "' + gKey + '", which is not allowed by the schema'); | ||
} | ||
return; // no reason to do more | ||
} | ||
if (gKey) { | ||
def = ss._schema[gKey]; | ||
val = this.value; | ||
// Filter out props if necessary; any property is OK for $unset because we want to | ||
// allow conversions to remove props that have been removed from the schema. | ||
if (options.filter && this.operator !== '$unset' && !ss.allowsKey(gKey) || options.removeNullsFromArrays && this.isArrayItem) { | ||
// XXX Special handling for $each; maybe this could be made nicer | ||
if (this.position.slice(-7) === '[$each]') { | ||
mDoc.removeValueForPosition(this.position.slice(0, -7)); | ||
removedPositions.push(this.position.slice(0, -7)); | ||
} else { | ||
this.remove(); | ||
removedPositions.push(this.position); | ||
} | ||
if (_SimpleSchema.SimpleSchema.debug) { | ||
console.info('SimpleSchema.clean: filtered out value that would have affected key "' + gKey + '", which is not allowed by the schema'); | ||
} | ||
return; // no reason to do more | ||
var outerDef = ss._schema[gKey]; | ||
var def = outerDef && outerDef.type.definitions[0]; | ||
// Autoconvert values if requested and if possible | ||
if (options.autoConvert && def) { | ||
var newVal = (0, _convertToProperType2.default)(val, def.type); | ||
if (newVal !== undefined && newVal !== val) { | ||
_SimpleSchema.SimpleSchema.debug && console.info('SimpleSchema.clean: autoconverted value ' + val + ' from ' + (typeof val === 'undefined' ? 'undefined' : _typeof(val)) + ' to ' + (typeof newVal === 'undefined' ? 'undefined' : _typeof(newVal)) + ' for ' + gKey); | ||
val = newVal; | ||
this.updateValue(newVal); | ||
} | ||
if (val !== void 0) { | ||
// Autoconvert values if requested and if possible | ||
if (options.autoConvert && this.operator !== '$unset' && def) { | ||
var newVal = typeconvert(val, def.type); | ||
if (newVal !== void 0 && newVal !== val) { | ||
_SimpleSchema.SimpleSchema.debug && console.info('SimpleSchema.clean: autoconverted value ' + val + ' from ' + (typeof val === 'undefined' ? 'undefined' : _typeof(val)) + ' to ' + (typeof newVal === 'undefined' ? 'undefined' : _typeof(newVal)) + ' for ' + gKey); | ||
val = newVal; | ||
this.updateValue(newVal); | ||
} | ||
} | ||
trimAndRemoveStrings.call(this); | ||
} | ||
// Trim strings if | ||
// 1. The trimStrings option is `true` AND | ||
// 2. The field is not in the schema OR is in the schema with `trim` !== `false` AND | ||
// 3. The value is a string. | ||
if (options.trimStrings && (!def || def.trim !== false) && typeof val === 'string') { | ||
val = val.trim(); | ||
this.updateValue(val); | ||
} | ||
// Remove empty strings if | ||
// 1. The removeEmptyStrings option is `true` AND | ||
// 2. The value is in a normal object or in the $set part of a modifier | ||
// 3. The value is an empty string. | ||
if (options.removeEmptyStrings && (!this.operator || this.operator === '$set') && typeof val === 'string' && !val.length) { | ||
// For a document, we remove any fields that are being set to an empty string | ||
this.remove(); | ||
// For a modifier, we $unset any fields that are being set to an empty string. | ||
// But only if we're not already within an entire object that is being set. | ||
if (this.operator === '$set' && this.position.match(/\[.+?\]/g).length < 2) { | ||
p = this.position.replace('$set', '$unset'); | ||
mongoObject.setValueForPosition(p, ''); | ||
} | ||
@@ -134,4 +160,4 @@ } | ||
var removedPositionParent = removedPosition.slice(0, lastBrace); | ||
var value = mDoc.getValueForPosition(removedPositionParent); | ||
if (_underscore2.default.isEmpty(value)) mDoc.removeValueForPosition(removedPositionParent); | ||
var value = mongoObject.getValueForPosition(removedPositionParent); | ||
if (_underscore2.default.isEmpty(value)) mongoObject.removeValueForPosition(removedPositionParent); | ||
} | ||
@@ -153,3 +179,3 @@ } catch (err) { | ||
mDoc.removeArrayItems(); | ||
mongoObject.removeArrayItems(); | ||
})(); | ||
@@ -159,3 +185,3 @@ } | ||
// Set automatic values | ||
options.getAutoValues && getAutoValues(ss, mDoc, options.isModifier, options.extendAutoValueContext); | ||
options.getAutoValues && (0, _setAutoValues2.default)(ss.autoValueFunctions(), mongoObject, options.isModifier, options.extendAutoValueContext); | ||
@@ -165,225 +191,10 @@ // Ensure we don't have any operators set to an empty object | ||
if (options.isModifier) { | ||
Object.keys(doc || {}).forEach(function (op) { | ||
if (_underscore2.default.isEmpty(doc[op])) delete doc[op]; | ||
Object.keys(cleanDoc || {}).forEach(function (op) { | ||
if (_underscore2.default.isEmpty(cleanDoc[op])) delete cleanDoc[op]; | ||
}); | ||
} | ||
return doc; | ||
return cleanDoc; | ||
} | ||
/* | ||
* PRIVATE | ||
*/ | ||
/** | ||
* Converts value to proper type | ||
* | ||
* @param {Any} value Value to try to convert | ||
* @param {Any} type A type | ||
* @returns {Any} Value converted to type. | ||
*/ | ||
function typeconvert(value, type) { | ||
// Can't and shouldn't convert arrays or objects | ||
if (Array.isArray(value) || value && (typeof value === 'function' || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') && !(value instanceof Date)) return value; | ||
// Convert to String type | ||
if (type === String) { | ||
if (value === null || value === undefined) return value; | ||
return value.toString(); | ||
} | ||
// Convert to Number type | ||
if (type === Number || type === _SimpleSchema.SimpleSchema.Integer) { | ||
if (typeof value === 'string' && value.length > 0) { | ||
// Try to convert numeric strings to numbers | ||
var numberVal = Number(value); | ||
if (!isNaN(numberVal)) return numberVal; | ||
} | ||
// Leave it; will fail validation | ||
return value; | ||
} | ||
// If target type is a Date we can safely convert from either a | ||
// number (Integer value representing the number of milliseconds | ||
// since 1 January 1970 00:00:00 UTC) or a string that can be parsed | ||
// by Date. | ||
if (type === Date) { | ||
if (typeof value === 'string') { | ||
var parsedDate = Date.parse(value); | ||
if (isNaN(parsedDate) === false) return new Date(parsedDate); | ||
} | ||
if (typeof value === 'number') return new Date(value); | ||
} | ||
// If an array is what you want, I'll give you an array | ||
if (type === Array) return [value]; | ||
// Could not convert | ||
return value; | ||
} | ||
/** | ||
* @method getAutoValues | ||
* @private | ||
* @param {SimpleSchema} ss - A SimpleSchema instance | ||
* @param {MongoObject} mDoc | ||
* @param {Boolean} [isModifier=false] - Is it a modifier doc? | ||
* @param {Object} [extendedAutoValueContext] - Object that will be added to the context when calling each autoValue function | ||
* @returns {undefined} | ||
* | ||
* Updates doc with automatic values from autoValue functions or default | ||
* values from defaultValue. Modifies the referenced object in place. | ||
*/ | ||
function getAutoValues(ss, mDoc, isModifier, extendedAutoValueContext) { | ||
var doneKeys = []; | ||
function runAV(func) { | ||
var affectedKey = this.key; | ||
// If already called for this key, skip it | ||
if (Array.includes(doneKeys, affectedKey)) return; | ||
var lastDot = affectedKey.lastIndexOf('.'); | ||
var fieldParentName = lastDot === -1 ? '' : affectedKey.slice(0, lastDot + 1); | ||
var doUnset = false; | ||
var autoValue = func.call(Object.assign({ | ||
isSet: this.value !== void 0, | ||
unset: function unset() { | ||
doUnset = true; | ||
}, | ||
value: this.value, | ||
operator: this.operator, | ||
field: function field(fName) { | ||
var keyInfo = mDoc.getInfoForKey(fName) || {}; | ||
return { | ||
isSet: keyInfo.value !== void 0, | ||
value: keyInfo.value, | ||
operator: keyInfo.operator || null | ||
}; | ||
}, | ||
siblingField: function siblingField(fName) { | ||
var keyInfo = mDoc.getInfoForKey(fieldParentName + fName) || {}; | ||
return { | ||
isSet: keyInfo.value !== void 0, | ||
value: keyInfo.value, | ||
operator: keyInfo.operator || null | ||
}; | ||
} | ||
}, extendedAutoValueContext || {}), mDoc.getObject()); | ||
// Update tracking of which keys we've run autovalue for | ||
doneKeys.push(affectedKey); | ||
if (doUnset) mDoc.removeValueForPosition(this.position); | ||
if (autoValue === void 0) return; | ||
// If the user's auto value is of the pseudo-modifier format, parse it | ||
// into operator and value. | ||
if (isModifier) { | ||
var op = void 0; | ||
var newValue = void 0; | ||
if (autoValue && (typeof autoValue === 'undefined' ? 'undefined' : _typeof(autoValue)) === 'object') { | ||
var avOperator = Object.keys(autoValue).find(function (avProp) { | ||
return avProp.substring(0, 1) === '$'; | ||
}); | ||
if (avOperator) { | ||
op = avOperator; | ||
newValue = autoValue[avOperator]; | ||
} | ||
} | ||
// Add $set for updates and upserts if necessary | ||
if (!op && isModifier && this.position.slice(0, 1) !== '$') { | ||
op = '$set'; | ||
newValue = autoValue; | ||
} | ||
if (op) { | ||
// Update/change value | ||
mDoc.removeValueForPosition(this.position); | ||
mDoc.setValueForPosition(op + '[' + affectedKey + ']', newValue); | ||
return; | ||
} | ||
} | ||
// Update/change value | ||
mDoc.setValueForPosition(this.position, autoValue); | ||
} | ||
_underscore2.default.each(ss._autoValues, function (func, fieldName) { | ||
// AV should run for the exact key only, for each array item if under array | ||
// should run whenever | ||
// 1 it is set | ||
// 2 it will be set by an ancestor field being set | ||
// 3 it is not set and is not within an array | ||
// 4 it is not set and is within an array, run for each array item that is set | ||
// 5 if doing $set[a.$] or $set[a.$.b] | ||
var test = fieldName; | ||
var positions = []; | ||
var lastDot = void 0; | ||
var lastDollar = fieldName.lastIndexOf('$'); | ||
var isOrIsWithinArray = lastDollar !== -1; | ||
// We always need to start by checking the array itself in case | ||
// it has items that have objects that need AV run. If not, we | ||
// will later check for the exact field name. | ||
if (isOrIsWithinArray) test = fieldName.slice(0, lastDollar + 1); | ||
while (positions.length === 0 && test.length > 0) { | ||
positions = mDoc.getPositionsInfoForGenericKey(test); | ||
if (positions.length > 0) { | ||
if (fieldName !== test) { | ||
if (fieldName.indexOf('.$.') > -1) { | ||
(function () { | ||
var lastPart = ''; | ||
if (fieldName.indexOf(test + '.') === 0) { | ||
lastPart = fieldName.replace(test + '.', ''); | ||
} | ||
positions = _underscore2.default.map(positions, function (position) { | ||
position.key = position.key + '.' + lastPart; | ||
position.position = position.position + '[' + lastPart + ']'; | ||
position.value = mDoc.getValueForPosition(position.position); | ||
return position; | ||
}); | ||
})(); | ||
} else { | ||
positions = []; | ||
break; | ||
} | ||
} | ||
} else { | ||
lastDot = test.lastIndexOf('.'); | ||
if (lastDot > -1) { | ||
test = test.slice(0, lastDot); | ||
} else { | ||
test = ''; | ||
} | ||
} | ||
} | ||
if (positions.length === 0) { | ||
if (isOrIsWithinArray) { | ||
positions = mDoc.getPositionsInfoForGenericKey(fieldName); | ||
} else { | ||
// Not set directly or indirectly | ||
positions.push({ | ||
key: fieldName, | ||
value: undefined, | ||
operator: isModifier ? '$set' : null, | ||
position: isModifier ? '$set[' + fieldName + ']' : _mongoObject2.default._keyToPosition(fieldName) | ||
}); | ||
} | ||
} | ||
// Run the autoValue function once for each place in the object that | ||
// has a value or that potentially should. | ||
_underscore2.default.each(positions, function (position) { | ||
runAV.call(position, func); | ||
}); | ||
}); | ||
} | ||
exports.default = clean; |
@@ -15,4 +15,2 @@ 'use strict'; | ||
var _utility = require('./utility.js'); | ||
var _underscore = require('underscore'); | ||
@@ -24,12 +22,32 @@ | ||
var _utility = require('./utility.js'); | ||
var _typeValidator = require('./validation/typeValidator'); | ||
var _typeValidator2 = _interopRequireDefault(_typeValidator); | ||
var _requiredValidator = require('./validation/requiredValidator'); | ||
var _requiredValidator2 = _interopRequireDefault(_requiredValidator); | ||
var _allowedValuesValidator = require('./validation/allowedValuesValidator'); | ||
var _allowedValuesValidator2 = _interopRequireDefault(_allowedValuesValidator); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function shouldCheck(key) { | ||
if (key === '$pushAll') throw new Error('$pushAll is not supported; use $push + $each'); | ||
return ['$pull', '$pullAll', '$pop', '$slice'].indexOf(key) === -1; | ||
} | ||
function doValidation(_ref) { | ||
var obj = _ref.obj; | ||
var extendedCustomContext = _ref.extendedCustomContext; | ||
var ignoreTypes = _ref.ignoreTypes; | ||
var isModifier = _ref.isModifier; | ||
var isUpsert = _ref.isUpsert; | ||
var keysToValidate = _ref.keysToValidate; | ||
var mongoObject = _ref.mongoObject; | ||
var obj = _ref.obj; | ||
var schema = _ref.schema; | ||
var extendedCustomContext = _ref.extendedCustomContext; | ||
var ignoreTypes = _ref.ignoreTypes; | ||
@@ -46,6 +64,5 @@ // First do some basic checks of the object, and throw errors if necessary | ||
var validationErrors = []; | ||
var mDoc = void 0; // for caching the MongoObject if necessary | ||
// Validation function called for each affected key | ||
function validate(val, affectedKey, affectedKeyGeneric, def, op, skipRequiredCheck, isInArrayItemObject, isInSubObject) { | ||
function validate(val, affectedKey, affectedKeyGeneric, def, op, isInArrayItemObject, isInSubObject) { | ||
// Get the schema for this key, marking invalid if there isn't one. | ||
@@ -61,23 +78,4 @@ if (!def) { | ||
// Check for missing required values. The general logic is this: | ||
// * If the operator is $unset or $rename, it's invalid. | ||
// * If the value is null, it's invalid. | ||
// * If the value is undefined and one of the following are true, it's invalid: | ||
// * We're validating a key of a sub-object. | ||
// * We're validating a key of an object that is an array item. | ||
// * We're validating a document (as opposed to a modifier). | ||
// * We're validating a key under the $set operator in a modifier, and it's an upsert. | ||
if (!skipRequiredCheck && !def.optional) { | ||
if (val === null || op === '$unset' || op === '$rename' || val === void 0 && (isInArrayItemObject || isInSubObject || !op || op === '$set')) { | ||
validationErrors.push({ | ||
name: affectedKey, | ||
type: _SimpleSchema.SimpleSchema.ErrorTypes.REQUIRED, | ||
value: null | ||
}); | ||
return; | ||
} | ||
} | ||
// For $rename, make sure that the new name is allowed by the schema | ||
if (op === '$rename' && typeof val === 'string' && !schema.allowsKey(val)) { | ||
if (op === '$rename' && !schema.allowsKey(val)) { | ||
validationErrors.push({ | ||
@@ -91,95 +89,126 @@ name: val, | ||
// Value checks are not necessary for null or undefined values, except for null array items, | ||
// or for $unset or $rename values | ||
if (op !== '$unset' && op !== '$rename' && (val !== undefined && val !== null || affectedKeyGeneric.slice(-2) === '.$' && val === null)) { | ||
// Check that value is of the correct type | ||
var typeError = doTypeChecks(def, val, op); | ||
if (typeError) { | ||
validationErrors.push(_extends({ | ||
name: affectedKey, | ||
value: val | ||
}, typeError)); | ||
return; | ||
} | ||
// Prepare the context object for the validator functions | ||
var fieldParentName = (0, _utility.getParentOfKey)(affectedKey, true); | ||
// Check value against allowedValues array | ||
if (def.allowedValues && !Array.includes(def.allowedValues, val)) { | ||
validationErrors.push({ | ||
name: affectedKey, | ||
type: _SimpleSchema.SimpleSchema.ErrorTypes.VALUE_NOT_ALLOWED, | ||
value: val | ||
}); | ||
return; | ||
} | ||
function getFieldInfo(key) { | ||
// Create mongoObject if necessary, cache for speed | ||
if (!mongoObject) mongoObject = new _mongoObject2.default(obj, schema.blackboxKeys()); | ||
var keyInfo = mongoObject.getInfoForKey(key) || {}; | ||
return { | ||
isSet: keyInfo.value !== undefined, | ||
value: keyInfo.value, | ||
operator: keyInfo.operator || null | ||
}; | ||
} | ||
// Perform custom validation | ||
var lastDot = affectedKey.lastIndexOf('.'); | ||
var fieldParentName = lastDot === -1 ? '' : affectedKey.slice(0, lastDot + 1); | ||
var validators = def.custom ? [def.custom] : []; | ||
validators = validators.concat(schema._validators).concat(_SimpleSchema.SimpleSchema._validators); | ||
_underscore2.default.every(validators, function (validator) { | ||
var result = validator.call(Object.assign({ | ||
key: affectedKey, | ||
genericKey: affectedKeyGeneric, | ||
definition: def, | ||
isSet: val !== undefined, | ||
value: val, | ||
operator: op, | ||
field: function field(fName) { | ||
mDoc = mDoc || new _mongoObject2.default(obj, schema._blackboxKeys); // create if necessary, cache for speed | ||
var keyInfo = mDoc.getInfoForKey(fName) || {}; | ||
return { | ||
isSet: keyInfo.value !== undefined, | ||
value: keyInfo.value, | ||
operator: keyInfo.operator | ||
}; | ||
}, | ||
siblingField: function siblingField(fName) { | ||
mDoc = mDoc || new _mongoObject2.default(obj, schema._blackboxKeys); // create if necessary, cache for speed | ||
var keyInfo = mDoc.getInfoForKey(fieldParentName + fName) || {}; | ||
return { | ||
isSet: keyInfo.value !== undefined, | ||
value: keyInfo.value, | ||
operator: keyInfo.operator | ||
}; | ||
}, | ||
addValidationErrors: function addValidationErrors(errors) { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
var fieldValidationErrors = []; | ||
var validatorContext = _extends({ | ||
addValidationErrors: function addValidationErrors(errors) { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = errors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var error = _step.value; | ||
fieldValidationErrors.push(error); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
for (var _iterator = errors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var error = _step.value; | ||
validationErrors.push(error); | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
}, extendedCustomContext || {})); | ||
if (typeof result === 'string') { | ||
validationErrors.push({ | ||
name: affectedKey, | ||
type: result, | ||
value: val | ||
}); | ||
return false; | ||
}, | ||
field: function field(fName) { | ||
return getFieldInfo(fName); | ||
}, | ||
genericKey: affectedKeyGeneric, | ||
isInArrayItemObject: isInArrayItemObject, | ||
isInSubObject: isInSubObject, | ||
isModifier: isModifier, | ||
isSet: val !== undefined, | ||
key: affectedKey, | ||
obj: obj, | ||
operator: op, | ||
siblingField: function siblingField(fName) { | ||
return getFieldInfo(fieldParentName + fName); | ||
}, | ||
value: val, | ||
// Value checks are not necessary for null or undefined values, | ||
// except for null array items, or for $unset or $rename values | ||
valueShouldBeChecked: op !== '$unset' && op !== '$rename' && (val !== undefined && val !== null || affectedKeyGeneric.slice(-2) === '.$' && val === null) | ||
}, extendedCustomContext || {}); | ||
var builtInValidators = [_requiredValidator2.default, _typeValidator2.default, _allowedValuesValidator2.default]; | ||
var validators = builtInValidators.concat(schema._validators).concat(_SimpleSchema.SimpleSchema._validators); | ||
// Loop through each of the definitions in the SimpleSchemaGroup. | ||
// If any return true, we're valid. | ||
var fieldIsValid = _underscore2.default.some(def.type, function (typeDef) { | ||
var finalValidatorContext = _extends({}, validatorContext, { | ||
// Take outer definition props like "optional" and "label" | ||
// and add them to inner props like "type" and "min" | ||
definition: _extends({}, _underscore2.default.omit(def, 'type'), typeDef) | ||
}); | ||
// Add custom field validators to the list after the built-in | ||
// validators but before the schema and global validators. | ||
var fieldValidators = validators.slice(0); | ||
if (typeof typeDef.custom === 'function') { | ||
fieldValidators.splice(builtInValidators.length, 0, typeDef.custom); | ||
} | ||
if (result === false) return false; | ||
return true; | ||
// We use _.every just so that we don't continue running more validator | ||
// functions after the first one returns false or an error string. | ||
return _underscore2.default.every(fieldValidators, function (validator) { | ||
var result = validator.call(finalValidatorContext); | ||
// If the validator returns a string, assume it is the | ||
// error type. | ||
if (typeof result === 'string') { | ||
fieldValidationErrors.push({ | ||
name: affectedKey, | ||
type: result, | ||
value: val | ||
}); | ||
return false; | ||
} | ||
// If the validator returns an object, assume it is an | ||
// error object. | ||
if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) === 'object' && result !== null) { | ||
fieldValidationErrors.push(_extends({ | ||
name: affectedKey, | ||
value: val | ||
}, result)); | ||
return false; | ||
} | ||
// If the validator returns false, assume they already | ||
// called this.addValidationErrors within the function | ||
if (result === false) return false; | ||
// Any other return value we assume means it was valid | ||
return true; | ||
}); | ||
}); | ||
if (!fieldIsValid) { | ||
validationErrors = validationErrors.concat(fieldValidationErrors); | ||
} | ||
} | ||
@@ -192,3 +221,2 @@ | ||
var operator = _ref2.operator; | ||
var setKeys = _ref2.setKeys; | ||
var _ref2$isInArrayItemOb = _ref2.isInArrayItemObject; | ||
@@ -212,3 +240,3 @@ var isInArrayItemObject = _ref2$isInArrayItemOb === undefined ? false : _ref2$isInArrayItemOb; | ||
var shouldValidateKey = !keysToValidate || _underscore2.default.any(keysToValidate, function (keyToValidate) { | ||
return keyToValidate === affectedKey || keyToValidate === affectedKeyGeneric || affectedKey.indexOf(keyToValidate + '.') === 0 || affectedKeyGeneric.indexOf(keyToValidate + '.') === 0; | ||
return keyToValidate === affectedKey || keyToValidate === affectedKeyGeneric || affectedKey.startsWith(keyToValidate + '.') || affectedKeyGeneric.startsWith(keyToValidate + '.'); | ||
}); | ||
@@ -218,16 +246,14 @@ | ||
if (shouldValidateKey) { | ||
// We can skip the required check for keys that are ancestors | ||
// of those in $set or $setOnInsert because they will be created | ||
// by MongoDB while setting. | ||
var skipRequiredCheck = _underscore2.default.some(setKeys, function (sk) { | ||
return sk.slice(0, affectedKey.length + 1) === affectedKey + '.'; | ||
}); | ||
validate(val, affectedKey, affectedKeyGeneric, def, operator, skipRequiredCheck, isInArrayItemObject, isInSubObject); | ||
validate(val, affectedKey, affectedKeyGeneric, def, operator, isInArrayItemObject, isInSubObject); | ||
} | ||
} | ||
// If affectedKeyGeneric is undefined due to this being the first run of this | ||
// function, objectKeys will return the top-level keys. | ||
var childKeys = schema.objectKeys(affectedKeyGeneric); | ||
// Temporarily convert missing objects to empty objects | ||
// so that the looping code will be called and required | ||
// descendent keys can be validated. | ||
if ((val === undefined || val === null) && (!def || def.type === Object && !def.optional)) { | ||
if ((val === undefined || val === null) && (!def || !def.optional && childKeys && childKeys.length > 0)) { | ||
val = {}; | ||
@@ -242,7 +268,6 @@ } | ||
affectedKey: affectedKey + '.' + i, | ||
operator: operator, | ||
setKeys: setKeys | ||
operator: operator | ||
}); | ||
}); | ||
} else if (_mongoObject2.default.isBasicObject(val) && (!def || !def.blackbox)) { | ||
} else if ((0, _utility.isObjectWeShouldTraverse)(val) && (!def || !def.blackbox)) { | ||
// Loop through object keys | ||
@@ -256,3 +281,3 @@ | ||
// any missing required keys, and to run any custom functions for other keys. | ||
var keysToCheck = _underscore2.default.union(presentKeys, schema.objectKeys(affectedKeyGeneric)); | ||
var keysToCheck = _underscore2.default.union(presentKeys, childKeys); | ||
@@ -269,3 +294,2 @@ // If this object is within an array, make sure we check for | ||
operator: operator, | ||
setKeys: setKeys, | ||
isInArrayItemObject: isInArrayItemObject, | ||
@@ -279,5 +303,2 @@ isInSubObject: true | ||
function checkModifier(mod) { | ||
// Get a list of all keys in $set and $setOnInsert combined, for use later | ||
var setKeys = Object.keys(mod.$set || {}).concat(Object.keys(mod.$setOnInsert || {})); | ||
// If this is an upsert, add all the $setOnInsert keys to $set; | ||
@@ -300,3 +321,3 @@ // since we don't know whether it will be an insert or update, we'll | ||
} | ||
if ((0, _utility.shouldCheck)(op)) { | ||
if (shouldCheck(op)) { | ||
// For an upsert, missing props would not be set if an insert is performed, | ||
@@ -307,3 +328,3 @@ // so we check them all with undefined value to force any 'required' checks to fail | ||
var presentKeys = Object.keys(opObj); | ||
_underscore2.default.each(schema.objectKeys(), function (schemaKey) { | ||
schema.objectKeys().forEach(function (schemaKey) { | ||
if (!Array.includes(presentKeys, schemaKey)) { | ||
@@ -313,4 +334,3 @@ checkObj({ | ||
affectedKey: schemaKey, | ||
operator: op, | ||
setKeys: setKeys | ||
operator: op | ||
}); | ||
@@ -332,4 +352,3 @@ } | ||
affectedKey: k, | ||
operator: op, | ||
setKeys: setKeys | ||
operator: op | ||
}); | ||
@@ -348,2 +367,10 @@ }); | ||
// Custom whole-doc validators | ||
var docValidators = schema._docValidators.concat(_SimpleSchema.SimpleSchema._docValidators); | ||
docValidators.forEach(function (func) { | ||
var errors = func(obj); | ||
if (!Array.isArray(errors)) throw new Error('Custom doc validator must return an array of error objects'); | ||
if (errors.length) validationErrors = validationErrors.concat(errors); | ||
}); | ||
var addedFieldNames = []; | ||
@@ -362,73 +389,2 @@ validationErrors = _underscore2.default.filter(validationErrors, function (errObj) { | ||
function doTypeChecks(def, keyValue, op) { | ||
var expectedType = def.type; | ||
if (expectedType === String) { | ||
// String checks | ||
if (typeof keyValue !== 'string') { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'String' }; | ||
} else if (def.max !== null && def.max < keyValue.length) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MAX_STRING, max: def.max }; | ||
} else if (def.min !== null && def.min > keyValue.length) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MIN_STRING, min: def.min }; | ||
} else if (def.regEx instanceof RegExp && !def.regEx.test(keyValue)) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.FAILED_REGULAR_EXPRESSION, regExp: def.regEx.toString() }; | ||
} else if (Array.isArray(def.regEx)) { | ||
var regExError = void 0; | ||
_underscore2.default.every(def.regEx, function (re) { | ||
if (!re.test(keyValue)) { | ||
regExError = { type: _SimpleSchema.SimpleSchema.ErrorTypes.FAILED_REGULAR_EXPRESSION, regExp: re.toString() }; | ||
return false; | ||
} | ||
return true; | ||
}); | ||
if (regExError) return regExError; | ||
} | ||
} else if (expectedType === Number || expectedType === _SimpleSchema.SimpleSchema.Integer) { | ||
// Number checks | ||
if (typeof keyValue !== 'number' || isNaN(keyValue)) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: expectedType === _SimpleSchema.SimpleSchema.Integer ? 'Integer' : 'Number' }; | ||
} else if (op !== '$inc' && def.max !== null && (!!def.exclusiveMax ? def.max <= keyValue : def.max < keyValue)) { | ||
return { type: !!def.exclusiveMax ? _SimpleSchema.SimpleSchema.ErrorTypes.MAX_NUMBER_EXCLUSIVE : _SimpleSchema.SimpleSchema.ErrorTypes.MAX_NUMBER, max: def.max }; | ||
} else if (op !== '$inc' && def.min !== null && (!!def.exclusiveMin ? def.min >= keyValue : def.min > keyValue)) { | ||
return { type: !!def.exclusiveMin ? _SimpleSchema.SimpleSchema.ErrorTypes.MIN_NUMBER_EXCLUSIVE : _SimpleSchema.SimpleSchema.ErrorTypes.MIN_NUMBER, min: def.min }; | ||
} else if (expectedType === _SimpleSchema.SimpleSchema.Integer && !Number.isInteger(keyValue)) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MUST_BE_INTEGER }; | ||
} | ||
} else if (expectedType === Boolean) { | ||
// Boolean checks | ||
if (typeof keyValue !== 'boolean') { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'Boolean' }; | ||
} | ||
} else if (expectedType === Object) { | ||
// Object checks | ||
if (!_mongoObject2.default.isBasicObject(keyValue)) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'Object' }; | ||
} | ||
} else if (expectedType === Array) { | ||
// Array checks | ||
if (!Array.isArray(keyValue)) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: 'Array' }; | ||
} else if (def.minCount !== null && keyValue.length < def.minCount) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MIN_COUNT, minCount: def.minCount }; | ||
} else if (def.maxCount !== null && keyValue.length > def.maxCount) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MAX_COUNT, maxCount: def.maxCount }; | ||
} | ||
} else if (expectedType instanceof Function || (0, _utility.safariBugFix)(expectedType)) { | ||
// Generic constructor checks | ||
if (!(keyValue instanceof expectedType)) return { type: _SimpleSchema.SimpleSchema.ErrorTypes.EXPECTED_TYPE, dataType: expectedType.name }; | ||
// Date checks | ||
if (expectedType === Date) { | ||
if (isNaN(keyValue.getTime())) return { type: _SimpleSchema.SimpleSchema.ErrorTypes.BAD_DATE }; | ||
if (_underscore2.default.isDate(def.min) && def.min.getTime() > keyValue.getTime()) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MIN_DATE, min: (0, _utility.dateToDateString)(def.min) }; | ||
} else if (_underscore2.default.isDate(def.max) && def.max.getTime() < keyValue.getTime()) { | ||
return { type: _SimpleSchema.SimpleSchema.ErrorTypes.MAX_DATE, max: (0, _utility.dateToDateString)(def.max) }; | ||
} | ||
} | ||
} | ||
} | ||
exports.default = doValidation; |
@@ -46,2 +46,8 @@ 'use strict'; | ||
var _expandShorthand = require('./expandShorthand'); | ||
var _expandShorthand2 = _interopRequireDefault(_expandShorthand); | ||
var _utility = require('./utility'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -52,11 +58,16 @@ | ||
// Exported for tests | ||
var schemaDefinitionOptions = exports.schemaDefinitionOptions = ['type', 'label', 'optional', 'required', 'min', 'max', 'minCount', 'maxCount', 'allowedValues', 'exclusiveMin', 'exclusiveMax', 'regEx', 'custom', 'blackbox', 'autoValue', 'defaultValue', 'tracker', 'trim', 'humanizeAutoLabels']; | ||
var schemaDefinitionOptions = exports.schemaDefinitionOptions = ['type', 'label', 'optional', 'required', 'autoValue', 'defaultValue']; | ||
var optionsThatCanBeFunction = ['label', 'optional', 'min', 'max', 'minCount', 'maxCount', 'allowedValues']; | ||
var oneOfProps = ['type', 'min', 'max', 'minCount', 'maxCount', 'allowedValues', 'exclusiveMin', 'exclusiveMax', 'regEx', 'custom', 'blackbox', 'trim']; | ||
var propsThatCanBeFunction = ['label', 'optional']; | ||
var oneOfPropsThatCanBeFunction = ['min', 'max', 'minCount', 'maxCount', 'allowedValues', 'exclusiveMin', 'exclusiveMax', 'regEx']; | ||
var regExpMessages = [{ exp: _regExp2.default.Email, msg: 'must be a valid email address' }, { exp: _regExp2.default.WeakEmail, msg: 'must be a valid email address' }, { exp: _regExp2.default.Domain, msg: 'must be a valid domain' }, { exp: _regExp2.default.WeakDomain, msg: 'must be a valid domain' }, { exp: _regExp2.default.IP, msg: 'must be a valid IPv4 or IPv6 address' }, { exp: _regExp2.default.IPv4, msg: 'must be a valid IPv4 address' }, { exp: _regExp2.default.IPv6, msg: 'must be a valid IPv6 address' }, { exp: _regExp2.default.Url, msg: 'must be a valid URL' }, { exp: _regExp2.default.Id, msg: 'must be a valid alphanumeric ID' }]; | ||
var SimpleSchema = function () { | ||
function SimpleSchema(schemas, options) { | ||
var _this = this; | ||
function SimpleSchema() { | ||
var schema = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
@@ -68,78 +79,18 @@ _classCallCheck(this, SimpleSchema); | ||
options = options || {}; | ||
schemas = schemas || {}; | ||
// Stash the options object | ||
this._constructorOptions = _extends({}, options); | ||
if (this._constructorOptions.humanizeAutoLabels !== false) this._constructorOptions.humanizeAutoLabels = true; | ||
if (options.humanizeAutoLabels !== false) options.humanizeAutoLabels = true; | ||
if (!Array.isArray(schemas)) schemas = [schemas]; | ||
// adjust and store a copy of the schema definitions | ||
this._schema = mergeSchemas(schemas); | ||
// store the list of defined keys for speedier checking | ||
this._schemaKeys = []; | ||
// store autoValue functions by key | ||
this._autoValues = {}; | ||
// store the list of blackbox keys for passing to MongoObject constructor | ||
this._blackboxKeys = []; | ||
// store the list of first level keys | ||
this._firstLevelSchemaKeys = []; | ||
// a place to store custom validators for this instance | ||
// Custom validators for this instance | ||
this._validators = []; | ||
this._docValidators = []; | ||
this._depsLabels = {}; | ||
_underscore2.default.each(this._schema, function (definition, fieldName) { | ||
if (definition instanceof _SimpleSchemaGroup2.default) { | ||
definition.definitions = definition.definitions.map(function (def) { | ||
return checkAndScrubDefinition(fieldName, def, options); | ||
}); | ||
} else { | ||
definition = checkAndScrubDefinition(fieldName, definition, options); | ||
} | ||
_this._schema[fieldName] = definition; | ||
// Keep list of all keys | ||
_this._schemaKeys.push(fieldName); | ||
// Keep list of all top level keys | ||
if (fieldName.indexOf('.') === -1) _this._firstLevelSchemaKeys.push(fieldName); | ||
// Initialize label reactive dependency | ||
if (options.tracker) { | ||
_this._depsLabels[fieldName] = new options.tracker.Dependency(); | ||
} | ||
// Keep list of all blackbox keys | ||
if (definition.blackbox === true) _this._blackboxKeys.push(fieldName); | ||
// Keep list of autoValue functions | ||
if (definition.autoValue) _this._autoValues[fieldName] = definition.autoValue; | ||
// Check array items | ||
if (definition.type === Array) { | ||
if (!_this._schema[fieldName + '.$']) throw new Error('Missing definition for key ' + fieldName + '.$'); | ||
// Set array item label to same as array label if array item label is missing | ||
if (!_this._schema[fieldName + '.$'].hasOwnProperty('label')) { | ||
_this._schema[fieldName + '.$'].label = _this._schema[fieldName].label; | ||
if (options.tracker) { | ||
_this._depsLabels[fieldName + '.$'] = new options.tracker.Dependency(); | ||
} | ||
} | ||
} | ||
}); | ||
// Store a list of all object keys | ||
this._objectKeys = getObjectKeys(this._schema, this._schemaKeys); | ||
// We will store named validation contexts here | ||
// Named validation contexts | ||
this._validationContexts = {}; | ||
this._constructorOptions = options; | ||
// Clone, expanding shorthand, and store the schema object in this._schema | ||
this._schema = {}; | ||
this.extend(schema); | ||
// Define default validation error messages | ||
this.messageBox = new _messageBox2.default({ | ||
@@ -196,14 +147,50 @@ initialLanguage: 'en', | ||
}, options.clean); | ||
this.version = SimpleSchema.version; | ||
} | ||
/** | ||
* @param {String} [key] One specific or generic key for which to get the schema | ||
* @returns {Object} The entire schema object or just the definition for one key | ||
*/ | ||
_createClass(SimpleSchema, [{ | ||
key: 'findFirstAncestorSimpleSchema', | ||
value: function findFirstAncestorSimpleSchema(key, func) { | ||
var _this = this; | ||
var genericKey = _mongoObject2.default.makeKeyGeneric(key); | ||
_createClass(SimpleSchema, [{ | ||
var foundSchema = false; | ||
(0, _utility.forEachKeyAncestor)(genericKey, function (ancestor) { | ||
if (foundSchema) return; // skip remaining once we've found it | ||
var def = _this._schema[ancestor]; | ||
if (!def) return; | ||
def.type.definitions.forEach(function (typeDef) { | ||
if (typeDef.type instanceof SimpleSchema) { | ||
func(typeDef.type, ancestor, genericKey.slice(ancestor.length + 1)); | ||
foundSchema = true; | ||
} | ||
}); | ||
}); | ||
return foundSchema; | ||
} | ||
/** | ||
* @param {String} [key] One specific or generic key for which to get the schema | ||
* @returns {Object} The entire schema object or just the definition for one key | ||
*/ | ||
}, { | ||
key: 'schema', | ||
value: function schema(key) { | ||
return key ? this._schema[_mongoObject2.default.makeKeyGeneric(key)] : this._schema; | ||
if (!key) return this._schema; | ||
var genericKey = _mongoObject2.default.makeKeyGeneric(key); | ||
var keySchema = this._schema[genericKey]; | ||
// If not defined in this schema, see if it's defined in a subschema | ||
if (!keySchema) { | ||
this.findFirstAncestorSimpleSchema(key, function (simpleSchema, ancestor, subSchemaKey) { | ||
keySchema = simpleSchema.schema(subSchemaKey); | ||
}); | ||
} | ||
return keySchema; | ||
} | ||
@@ -232,12 +219,87 @@ | ||
_underscore2.default.each(defs, function (val, prop) { | ||
result[prop] = val; | ||
if (optionsThatCanBeFunction.indexOf(prop) > -1 && typeof result[prop] === 'function') { | ||
result[prop] = result[prop].call(functionContext || {}); | ||
if (propsThatCanBeFunction.indexOf(prop) > -1 && typeof val === 'function') { | ||
result[prop] = val.call(functionContext || {}); | ||
// Inflect label if undefined | ||
if (prop === 'label' && typeof result[prop] !== 'string') result[prop] = inflectedLabel(key, _this2._constructorOptions.humanizeAutoLabels); | ||
} else { | ||
result[prop] = val; | ||
} | ||
}); | ||
// Resolve all the types and convert to a normal array to make it easier | ||
// to use. | ||
if (defs.type) { | ||
result.type = defs.type.definitions.map(function (typeDef) { | ||
var newTypeDef = {}; | ||
_underscore2.default.each(typeDef, function (val, prop) { | ||
if (oneOfPropsThatCanBeFunction.indexOf(prop) > -1 && typeof val === 'function') { | ||
newTypeDef[prop] = val.call(functionContext || {}); | ||
} else { | ||
newTypeDef[prop] = val; | ||
} | ||
}); | ||
return newTypeDef; | ||
}); | ||
} | ||
return result; | ||
} | ||
// Returns an array of all the autovalue functions, including those in subschemas all the | ||
// way down the schema tree | ||
}, { | ||
key: 'autoValueFunctions', | ||
value: function autoValueFunctions() { | ||
var result = []; | ||
function addFuncs(autoValues, closestSubschemaFieldName) { | ||
_underscore2.default.each(autoValues, function (func, fieldName) { | ||
result.push({ | ||
func: func, | ||
fieldName: fieldName, | ||
closestSubschemaFieldName: closestSubschemaFieldName | ||
}); | ||
}); | ||
} | ||
addFuncs(this._autoValues, ''); | ||
_underscore2.default.each(this._schema, function (keySchema, key) { | ||
keySchema.type.definitions.forEach(function (typeDef) { | ||
if (!(typeDef.type instanceof SimpleSchema)) return; | ||
result = result.concat(typeDef.type.autoValueFunctions().map(function (_ref2) { | ||
var func = _ref2.func; | ||
var fieldName = _ref2.fieldName; | ||
var closestSubschemaFieldName = _ref2.closestSubschemaFieldName; | ||
return { | ||
func: func, | ||
fieldName: key + '.' + fieldName, | ||
closestSubschemaFieldName: closestSubschemaFieldName.length ? key + '.' + closestSubschemaFieldName : key | ||
}; | ||
})); | ||
}); | ||
}); | ||
return result; | ||
} | ||
// Returns an array of all the blackbox keys, including those in subschemas | ||
}, { | ||
key: 'blackboxKeys', | ||
value: function blackboxKeys() { | ||
var blackboxKeys = this._blackboxKeys; | ||
_underscore2.default.each(this._schema, function (keySchema, key) { | ||
keySchema.type.definitions.forEach(function (typeDef) { | ||
if (!(typeDef.type instanceof SimpleSchema)) return; | ||
typeDef.type._blackboxKeys.forEach(function (blackboxKey) { | ||
blackboxKeys.push(key + '.' + blackboxKey); | ||
}); | ||
}); | ||
}); | ||
return _underscore2.default.uniq(blackboxKeys); | ||
} | ||
// Check if the key is a nested dot-syntax key inside of a blackbox object | ||
@@ -248,15 +310,19 @@ | ||
value: function keyIsInBlackBox(key) { | ||
var testKey = _mongoObject2.default.makeKeyGeneric(key); | ||
var lastDot = void 0; | ||
var _this3 = this; | ||
// Iterate the dot-syntax hierarchy until we find a key in our schema | ||
do { | ||
lastDot = testKey.lastIndexOf('.'); | ||
if (lastDot !== -1) { | ||
testKey = testKey.slice(0, lastDot); // Remove last path component | ||
if (this._blackboxKeys.indexOf(testKey) > -1) return true; | ||
var isInBlackBox = false; | ||
(0, _utility.forEachKeyAncestor)(_mongoObject2.default.makeKeyGeneric(key), function (ancestor, remainder) { | ||
if (_this3._blackboxKeys.indexOf(ancestor) > -1) { | ||
isInBlackBox = true; | ||
} else { | ||
var testKeySchema = _this3.schema(ancestor); | ||
if (testKeySchema) { | ||
testKeySchema.type.definitions.forEach(function (typeDef) { | ||
if (!(typeDef.type instanceof SimpleSchema)) return; | ||
if (typeDef.type.keyIsInBlackBox(remainder)) isInBlackBox = true; | ||
}); | ||
} | ||
} | ||
} while (lastDot !== -1); | ||
return false; | ||
}); | ||
return isInBlackBox; | ||
} | ||
@@ -271,25 +337,33 @@ | ||
value: function allowsKey(key) { | ||
var _this3 = this; | ||
var _this4 = this; | ||
// Loop through all keys in the schema | ||
return _underscore2.default.any(this._schemaKeys, function (schemaKey) { | ||
return _underscore2.default.any(this._schemaKeys, function (loopKey) { | ||
// If the schema key is the test key, it's allowed. | ||
if (schemaKey === key) return true; | ||
if (loopKey === key) return true; | ||
var fieldSchema = _this4.schema(loopKey); | ||
var compare1 = key.slice(0, loopKey.length + 2); | ||
var compare2 = compare1.slice(0, -1); | ||
// Blackbox and subschema checks are needed only if key starts with | ||
// loopKey + a dot | ||
if (compare2 !== loopKey + '.') return false; | ||
// Black box handling | ||
if (_this3.schema(schemaKey).blackbox === true) { | ||
var kl = schemaKey.length; | ||
var compare1 = key.slice(0, kl + 2); | ||
var compare2 = compare1.slice(0, -1); | ||
if (fieldSchema.blackbox === true) { | ||
// If the test key is the black box key + ".$", then the test | ||
// key is NOT allowed because black box keys are by definition | ||
// only for objects, and not for arrays. | ||
if (compare1 === schemaKey + '.$') return false; | ||
// Otherwise | ||
if (compare2 === schemaKey + '.') return true; | ||
return compare1 !== loopKey + '.$'; | ||
} | ||
return false; | ||
// Subschemas | ||
var allowed = false; | ||
var subKey = key.slice(loopKey.length + 1); | ||
fieldSchema.type.definitions.forEach(function (typeDef) { | ||
if (!(typeDef.type instanceof SimpleSchema)) return; | ||
if (typeDef.type.allowsKey(subKey)) allowed = true; | ||
}); | ||
return allowed; | ||
}); | ||
@@ -316,4 +390,4 @@ } | ||
* | ||
* @param {SimpleSchema} schema | ||
* @returns {SimpleSchema} The new, extended schema. | ||
* @param {SimpleSchema|Object} schema | ||
* @returns undefined | ||
*/ | ||
@@ -323,4 +397,59 @@ | ||
key: 'extend', | ||
value: function extend(schema) { | ||
return new SimpleSchema([this, schema]); | ||
value: function extend() { | ||
var _this5 = this; | ||
var schema = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var schemaObj = void 0; | ||
if (schema instanceof SimpleSchema) { | ||
schemaObj = schema._schema; | ||
// Merge the validators | ||
this._validators = this._validators.concat(schema._validators); | ||
this._docValidators = this._docValidators.concat(schema._docValidators); | ||
} else { | ||
schemaObj = (0, _expandShorthand2.default)(schema); | ||
} | ||
// Extend this._schema with additional fields and definitions from schema | ||
this._schema = mergeSchemas([this._schema, schemaObj]); | ||
checkSchemaOverlap(this._schema); | ||
// Set/Reset all of these | ||
this._schemaKeys = []; | ||
this._autoValues = {}; | ||
this._blackboxKeys = []; | ||
this._firstLevelSchemaKeys = []; | ||
this._depsLabels = {}; | ||
this._objectKeys = {}; | ||
// Update all of the information cached on the instance | ||
_underscore2.default.each(this._schema, function (definition, fieldName) { | ||
_this5._schema[fieldName] = definition = checkAndScrubDefinition(fieldName, definition, _this5._constructorOptions, _this5._schema); | ||
// Keep list of all keys for speedier checking | ||
_this5._schemaKeys.push(fieldName); | ||
// Keep list of all top level keys | ||
if (fieldName.indexOf('.') === -1) _this5._firstLevelSchemaKeys.push(fieldName); | ||
// Initialize label reactive dependency (Meteor only) | ||
if (_this5._constructorOptions.tracker) { | ||
_this5._depsLabels[fieldName] = new _this5._constructorOptions.tracker.Dependency(); | ||
} | ||
// Keep list of all blackbox keys for passing to MongoObject constructor | ||
if (definition.blackbox) _this5._blackboxKeys.push(fieldName); | ||
// Keep list of autoValue functions by key | ||
if (definition.autoValue) _this5._autoValues[fieldName] = definition.autoValue; | ||
// Store child keys keyed by parent | ||
if (fieldName.indexOf('.') > -1 && fieldName.slice(-2) !== '.$') { | ||
var parentKey = fieldName.slice(0, fieldName.lastIndexOf('.')); | ||
var parentKeyWithDot = parentKey + '.'; | ||
_this5._objectKeys[parentKeyWithDot] = _this5._objectKeys[parentKeyWithDot] || []; | ||
_this5._objectKeys[parentKeyWithDot].push(fieldName.slice(fieldName.lastIndexOf('.') + 1)); | ||
} | ||
}); | ||
} | ||
@@ -347,4 +476,19 @@ }, { | ||
}, { | ||
key: 'addDocValidator', | ||
value: function addDocValidator(func) { | ||
this._docValidators.push(func); | ||
} | ||
/** | ||
* @param obj {Object|Object[]} Object or array of objects to validate. | ||
* @param [options] {Object} Same options object that ValidationContext#validate takes | ||
* | ||
* Throws an Error with name `ClientError` and `details` property containing the errors. | ||
*/ | ||
}, { | ||
key: 'validate', | ||
value: function validate(obj, options) { | ||
var _this6 = this; | ||
// For Meteor apps, `check` option can be passed to silence audit-argument-checks | ||
@@ -358,18 +502,31 @@ if (typeof this._constructorOptions.check === 'function') { | ||
var validationContext = this.newContext(); | ||
var isValid = validationContext.validate(obj, options); | ||
// obj can be an array, in which case we validate each object in it and | ||
// throw as soon as one has an error | ||
var objects = Array.isArray(obj) ? obj : [obj]; | ||
objects.forEach(function (oneObj) { | ||
var validationContext = _this6.newContext(); | ||
var isValid = validationContext.validate(oneObj, options); | ||
if (isValid) return; | ||
if (isValid) return; | ||
var errors = validationContext.validationErrors(); | ||
var errors = validationContext.validationErrors(); | ||
// In order for the message at the top of the stack trace to be useful, | ||
// we set it to the first validation error message. | ||
var message = this.messageForError(errors[0]); | ||
// In order for the message at the top of the stack trace to be useful, | ||
// we set it to the first validation error message. | ||
var message = _this6.messageForError(errors[0]); | ||
var error = new Error(message); | ||
error.name = error.errorType = 'ClientError'; | ||
error.details = errors; | ||
error.error = 'validation-error'; | ||
throw error; | ||
var error = new Error(message); | ||
error.name = error.errorType = 'ClientError'; | ||
error.details = errors; | ||
error.error = 'validation-error'; | ||
// The primary use for the validationErrorTransform is for the Meteor package | ||
// to convert the vanilla Error into a Meteor.Error until DDP is able to pass | ||
// vanilla errors back to the client. | ||
if (typeof SimpleSchema.validationErrorTransform === 'function') { | ||
throw SimpleSchema.validationErrorTransform(error); | ||
} else { | ||
throw error; | ||
} | ||
}); | ||
} | ||
@@ -379,3 +536,3 @@ }, { | ||
value: function validator() { | ||
var _this4 = this; | ||
var _this7 = this; | ||
@@ -385,4 +542,9 @@ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
return function (obj) { | ||
if (options.clean === true) _this4.clean(obj, options); | ||
_this4.validate(obj, options); | ||
var optionsClone = _extends({}, options); | ||
if (options.clean === true) { | ||
// Do this here and pass into both functions for better performance | ||
optionsClone.mongoObject = new _mongoObject2.default(obj, _this7.blackboxKeys()); | ||
_this7.clean(obj, optionsClone); | ||
} | ||
_this7.validate(obj, optionsClone); | ||
}; | ||
@@ -410,10 +572,10 @@ } | ||
value: function labels(_labels) { | ||
var _this5 = this; | ||
var _this8 = this; | ||
_underscore2.default.each(_labels, function (label, key) { | ||
if (typeof label !== 'string' && typeof label !== 'function') return; | ||
if (!_this5._schema.hasOwnProperty(key)) return; | ||
if (!_this8._schema.hasOwnProperty(key)) return; | ||
_this5._schema[key].label = label; | ||
_this5._depsLabels[key] && _this5._depsLabels[key].changed(); | ||
_this8._schema[key].label = label; | ||
_this8._depsLabels[key] && _this8._depsLabels[key].changed(); | ||
}); | ||
@@ -433,3 +595,3 @@ } | ||
value: function label(key) { | ||
var _this6 = this; | ||
var _this9 = this; | ||
@@ -440,4 +602,4 @@ // Get all labels | ||
var result = {}; | ||
_underscore2.default.each(_this6._schemaKeys, function (schemaKey) { | ||
result[schemaKey] = _this6.label(schemaKey); | ||
_underscore2.default.each(_this9._schemaKeys, function (schemaKey) { | ||
result[schemaKey] = _this9.label(schemaKey); | ||
}); | ||
@@ -506,2 +668,10 @@ return { | ||
}, { | ||
key: 'defineValidationErrorTransform', | ||
value: function defineValidationErrorTransform(transform) { | ||
if (typeof transform !== 'function') { | ||
throw new Error('SimpleSchema.defineValidationErrorTransform must be passed a function that accepts an Error and returns an Error'); | ||
} | ||
SimpleSchema.validationErrorTransform = transform; | ||
} | ||
}, { | ||
key: 'validate', | ||
@@ -518,4 +688,8 @@ value: function validate(obj, schema, options) { | ||
key: 'oneOf', | ||
value: function oneOf(definitions) { | ||
return new _SimpleSchemaGroup2.default(definitions); | ||
value: function oneOf() { | ||
for (var _len2 = arguments.length, definitions = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
definitions[_key2] = arguments[_key2]; | ||
} | ||
return new (Function.prototype.bind.apply(_SimpleSchemaGroup2.default, [null].concat(definitions)))(); | ||
} | ||
@@ -530,2 +704,7 @@ | ||
} | ||
}, { | ||
key: 'addDocValidator', | ||
value: function addDocValidator(func) { | ||
SimpleSchema._docValidators.push(func); | ||
} | ||
@@ -543,4 +722,6 @@ // Backwards compatibility | ||
SimpleSchema.version = 2; | ||
SimpleSchema.RegEx = _regExp2.default; | ||
SimpleSchema._validators = []; | ||
SimpleSchema._docValidators = []; | ||
SimpleSchema.ErrorTypes = { | ||
@@ -569,15 +750,4 @@ REQUIRED: 'required', | ||
function mergeSchemas(schemas) { | ||
// Merge all provided schema definitions. | ||
// This is effectively a shallow clone of each object, too, | ||
// which is what we want since we are going to manipulate it. | ||
var mergedSchema = {}; | ||
_underscore2.default.each(schemas, function (schema) { | ||
// Create a temporary SS instance so that the internal object | ||
// we use for merging/extending will be fully expanded | ||
if (schema instanceof SimpleSchema) { | ||
schema = schema._schema; | ||
} else { | ||
schema = expandSchema(schema); | ||
} | ||
// Loop through and extend each individual field | ||
@@ -597,106 +767,21 @@ // definition. That way you can extend and overwrite | ||
}); | ||
return mergedSchema; | ||
} | ||
// Returns an object relating the keys in the list | ||
// to their parent object. | ||
function getObjectKeys(schema, schemaKeyList) { | ||
var result = {}; | ||
// Throws an error if any fields are `type` SimpleSchema but then also | ||
// have subfields defined outside of that. | ||
function checkSchemaOverlap(schema) { | ||
_underscore2.default.each(schema, function (val, key) { | ||
_underscore2.default.each(val.type.definitions, function (def) { | ||
if (!(def.type instanceof SimpleSchema)) return; | ||
console.log(key, def.type); | ||
_underscore2.default.each(schema, function (definition, key) { | ||
if (definition.type !== Object) return; | ||
var keyPrefix = key + '.'; | ||
var childKeys = []; | ||
_underscore2.default.each(schemaKeyList, function (otherKey) { | ||
// Is it a descendant key? | ||
if (!otherKey.startsWith(keyPrefix)) return; | ||
var remainingText = otherKey.substring(keyPrefix.length); | ||
// Is it a direct child? | ||
if (remainingText.indexOf('.') === -1) childKeys.push(remainingText); | ||
}); | ||
result[keyPrefix] = childKeys; | ||
}); | ||
return result; | ||
} | ||
function expandSchema(schema) { | ||
// BEGIN SHORTHAND | ||
var addArrayFields = []; | ||
_underscore2.default.each(schema, function (definition, key) { | ||
if (!_mongoObject2.default.isBasicObject(definition) && !(definition instanceof _SimpleSchemaGroup2.default)) { | ||
if (Array.isArray(definition)) { | ||
if (Array.isArray(definition[0])) { | ||
throw new Error('Array shorthand may only be used to one level of depth (' + key + ')'); | ||
_underscore2.default.each(def.type._schema, function (subVal, subKey) { | ||
var newKey = key + '.' + subKey; | ||
if (schema.hasOwnProperty(newKey)) { | ||
throw new Error('The type for "' + key + '" is set to a SimpleSchema instance that defines "' + key + '.' + subKey + '", but the parent SimpleSchema instance also tries to define "' + key + '.' + subKey + '"'); | ||
} | ||
var type = definition[0]; | ||
if (type instanceof RegExp) { | ||
addArrayFields.push({ key: key, type: String, regEx: type }); | ||
} else { | ||
addArrayFields.push({ key: key, type: type }); | ||
} | ||
schema[key] = { type: Array }; | ||
} else { | ||
if (definition instanceof RegExp) { | ||
schema[key] = { | ||
type: String, | ||
regEx: definition | ||
}; | ||
} else { | ||
schema[key] = { type: definition }; | ||
} | ||
} | ||
} | ||
}); | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = addArrayFields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _step$value = _step.value; | ||
var key = _step$value.key; | ||
var type = _step$value.type; | ||
var regEx = _step$value.regEx; | ||
var itemKey = key + '.$'; | ||
if (schema[itemKey]) { | ||
throw new Error('Array shorthand used for ' + key + ' field but ' + key + '.$ key is already in the schema'); | ||
} | ||
schema[itemKey] = { type: type }; | ||
if (regEx) schema[itemKey].regEx = regEx; | ||
} | ||
// END SHORTHAND | ||
// Flatten schema by inserting nested definitions | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
_underscore2.default.each(schema, function (val, key) { | ||
if (!(val.type instanceof SimpleSchema)) return; | ||
// Add child schema definitions to parent schema | ||
_underscore2.default.each(val.type._schema, function (subVal, subKey) { | ||
var newKey = key + '.' + subKey; | ||
if (!schema.hasOwnProperty(newKey)) schema[newKey] = subVal; | ||
}); | ||
}); | ||
val.type = Object; | ||
}); | ||
return schema; | ||
} | ||
@@ -728,22 +813,45 @@ | ||
function checkAndScrubDefinition(fieldName, definition, options) { | ||
function checkAndScrubDefinition(fieldName, definition, options, fullSchemaObj) { | ||
var internalDefinition = _extends({}, definition); | ||
// Internally, all definition types are stored as groups for simplicity of access | ||
if (!(internalDefinition.type instanceof _SimpleSchemaGroup2.default)) { | ||
internalDefinition.type = new _SimpleSchemaGroup2.default(_underscore2.default.pick(internalDefinition, oneOfProps)); | ||
} | ||
// Limit to only the non-oneOf props | ||
internalDefinition = _underscore2.default.omit(internalDefinition, _underscore2.default.without(oneOfProps, 'type')); | ||
// Validate the field definition | ||
_underscore2.default.each(definition, function (val, key) { | ||
_underscore2.default.each(internalDefinition, function (val, key) { | ||
if (schemaDefinitionOptions.indexOf(key) === -1) { | ||
throw new Error('Invalid definition for ' + fieldName + ' field: "' + key + '" is not a supported option'); | ||
throw new Error('Invalid definition for ' + fieldName + ' field: "' + key + '" is not a supported property'); | ||
} | ||
}); | ||
// TYPE | ||
// Since type can be anything, make sure it's defined | ||
if (!definition.type) throw new Error('Invalid definition for ' + fieldName + ' field: "type" option is required'); | ||
// Make sure the `type`s are OK | ||
internalDefinition.type.definitions.forEach(function (_ref3) { | ||
var blackbox = _ref3.blackbox; | ||
var type = _ref3.type; | ||
if (Array.isArray(definition.type)) { | ||
throw new Error('Invalid definition for ' + fieldName + ' field: "type" may not be an array. Change it to Array.'); | ||
} | ||
if (!type) throw new Error('Invalid definition for ' + fieldName + ' field: "type" option is required'); | ||
var internalDefinition = _extends({}, definition); | ||
if (Array.isArray(type)) { | ||
throw new Error('Invalid definition for ' + fieldName + ' field: "type" may not be an array. Change it to Array.'); | ||
} | ||
// AUTOVALUE | ||
if (type instanceof SimpleSchema) { | ||
_underscore2.default.each(type._schema, function (subVal, subKey) { | ||
var newKey = fieldName + '.' + subKey; | ||
if (fullSchemaObj.hasOwnProperty(newKey)) { | ||
throw new Error('The type for "' + fieldName + '" is set to a SimpleSchema instance that defines "' + newKey + '", but the parent SimpleSchema instance also tries to define "' + newKey + '"'); | ||
} | ||
}); | ||
} | ||
// If any of the valid types are blackbox, mark blackbox on the overall definition | ||
if (blackbox === true) internalDefinition.blackbox = true; | ||
}); | ||
// defaultValue -> autoValue | ||
// We support defaultValue shortcut by converting it immediately into an | ||
@@ -771,4 +879,4 @@ // autoValue. | ||
internalDefinition.optional = function optional() { | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
args[_key3] = arguments[_key3]; | ||
} | ||
@@ -805,4 +913,4 @@ | ||
return function pickOrOmit() { | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
args[_key3] = arguments[_key3]; | ||
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { | ||
args[_key4] = arguments[_key4]; | ||
} | ||
@@ -809,0 +917,0 @@ |
@@ -19,2 +19,21 @@ 'use strict'; | ||
function isObjectWeShouldTraverse(val) { | ||
if (val !== Object(val)) return false; | ||
// There are some object types that we know we shouldn't traverse because | ||
// they will often result in overflows and it makes no sense to validate them. | ||
if (val instanceof Date) return false; | ||
if (val instanceof Int8Array) return false; | ||
if (val instanceof Uint8Array) return false; | ||
if (val instanceof Uint8ClampedArray) return false; | ||
if (val instanceof Int16Array) return false; | ||
if (val instanceof Uint16Array) return false; | ||
if (val instanceof Int32Array) return false; | ||
if (val instanceof Uint32Array) return false; | ||
if (val instanceof Float32Array) return false; | ||
if (val instanceof Float64Array) return false; | ||
return true; | ||
} | ||
function looksLikeModifier(obj) { | ||
@@ -26,11 +45,29 @@ return !!Object.keys(obj || {}).find(function (key) { | ||
// The latest Safari returns false for Uint8Array, etc. instanceof Function | ||
// unlike other browsers. | ||
function safariBugFix(type) { | ||
return typeof Uint8Array !== 'undefined' && type === Uint8Array || typeof Uint16Array !== 'undefined' && type === Uint16Array || typeof Uint32Array !== 'undefined' && type === Uint32Array || typeof Uint8ClampedArray !== 'undefined' && type === Uint8ClampedArray; | ||
/** | ||
* Run loopFunc for each ancestor key in a dot-delimited key. For example, | ||
* if key is "a.b.c", loopFunc will be called first with ('a.b', 'c') and then with ('a', 'b.c') | ||
*/ | ||
function forEachKeyAncestor(key, loopFunc) { | ||
var lastDot = void 0; | ||
// Iterate the dot-syntax hierarchy | ||
var ancestor = key; | ||
do { | ||
lastDot = ancestor.lastIndexOf('.'); | ||
if (lastDot !== -1) { | ||
ancestor = ancestor.slice(0, lastDot); | ||
var remainder = key.slice(ancestor.length + 1); | ||
loopFunc(ancestor, remainder); // Remove last path component | ||
} | ||
} while (lastDot !== -1); | ||
} | ||
function shouldCheck(key) { | ||
if (key === '$pushAll') throw new Error('$pushAll is not supported; use $push + $each'); | ||
return ['$pull', '$pullAll', '$pop', '$slice'].indexOf(key) === -1; | ||
/** | ||
* Returns the parent of a key. For example, returns 'a.b' when passed 'a.b.c'. | ||
* If no parent, returns an empty string. If withEndDot is true, the return | ||
* value will have a dot appended when it isn't an empty string. | ||
*/ | ||
function getParentOfKey(key, withEndDot) { | ||
var lastDot = key.lastIndexOf('.'); | ||
return lastDot === -1 ? '' : key.slice(0, lastDot + Number(!!withEndDot)); | ||
} | ||
@@ -40,4 +77,5 @@ | ||
exports.dateToDateString = dateToDateString; | ||
exports.isObjectWeShouldTraverse = isObjectWeShouldTraverse; | ||
exports.looksLikeModifier = looksLikeModifier; | ||
exports.safariBugFix = safariBugFix; | ||
exports.shouldCheck = shouldCheck; | ||
exports.forEachKeyAncestor = forEachKeyAncestor; | ||
exports.getParentOfKey = getParentOfKey; |
@@ -206,24 +206,25 @@ 'use strict'; | ||
var _ref$modifier = _ref.modifier; | ||
var modifier = _ref$modifier === undefined ? false : _ref$modifier; | ||
var _ref$upsert = _ref.upsert; | ||
var upsert = _ref$upsert === undefined ? false : _ref$upsert; | ||
var _ref$extendedCustomCo = _ref.extendedCustomContext; | ||
var extendedCustomContext = _ref$extendedCustomCo === undefined ? {} : _ref$extendedCustomCo; | ||
var _ref$ignore = _ref.ignore; | ||
var ignore = _ref$ignore === undefined ? [] : _ref$ignore; | ||
var _ref$keys = _ref.keys; | ||
var keys = _ref$keys === undefined ? null : _ref$keys; | ||
var ignoreTypes = _ref$ignore === undefined ? [] : _ref$ignore; | ||
var keysToValidate = _ref.keys; | ||
var _ref$modifier = _ref.modifier; | ||
var isModifier = _ref$modifier === undefined ? false : _ref$modifier; | ||
var mongoObject = _ref.mongoObject; | ||
var _ref$upsert = _ref.upsert; | ||
var isUpsert = _ref$upsert === undefined ? false : _ref$upsert; | ||
var validationErrors = (0, _doValidation2.default)({ | ||
extendedCustomContext: extendedCustomContext, | ||
ignoreTypes: ignoreTypes, | ||
isModifier: isModifier, | ||
isUpsert: isUpsert, | ||
keysToValidate: keysToValidate, | ||
mongoObject: mongoObject, | ||
obj: obj, | ||
isModifier: modifier, | ||
isUpsert: upsert, | ||
keysToValidate: keys, | ||
schema: this._simpleSchema, | ||
extendedCustomContext: extendedCustomContext, | ||
ignoreTypes: ignore | ||
schema: this._simpleSchema | ||
}); | ||
if (keys) { | ||
if (keysToValidate) { | ||
// We have only revalidated the listed keys, so if there | ||
@@ -240,3 +241,3 @@ // are any other existing errors that are NOT in the keys list, | ||
var wasValidated = _underscore2.default.any(keys, function (key) { | ||
var wasValidated = _underscore2.default.any(keysToValidate, function (key) { | ||
return key === error.name || error.name.startsWith(key + '.'); | ||
@@ -243,0 +244,0 @@ }); |
{ | ||
"name": "simpl-schema", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "A schema validation package that supports direct validation of MongoDB update modifier objects.", | ||
@@ -5,0 +5,0 @@ "author": "Eric Dobbertin <aldeed@gmail.com>", |
@@ -16,2 +16,75 @@ # simple-schema | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* | ||
- [simple-schema](#simple-schema) | ||
- [The History of SimpleSchema](#the-history-of-simpleschema) | ||
- [Installation](#installation) | ||
- [Lingo](#lingo) | ||
- [Quick Start](#quick-start) | ||
- [Validate an Object and Throw an Error](#validate-an-object-and-throw-an-error) | ||
- [Validate an Array of Objects and Throw an Error](#validate-an-array-of-objects-and-throw-an-error) | ||
- [Validate an Object and Get the Errors](#validate-an-object-and-get-the-errors) | ||
- [Validate a MongoDB Modifier](#validate-a-mongodb-modifier) | ||
- [Enable Meteor Tracker Reactivity](#enable-meteor-tracker-reactivity) | ||
- [Automatically Clean the Object Before Validating It](#automatically-clean-the-object-before-validating-it) | ||
- [Explicitly Clean an Object](#explicitly-clean-an-object) | ||
- [Defining a Schema](#defining-a-schema) | ||
- [Shorthand Definitions](#shorthand-definitions) | ||
- [Longhand Definitions](#longhand-definitions) | ||
- [Mixing Shorthand with Longhand](#mixing-shorthand-with-longhand) | ||
- [More Shorthand](#more-shorthand) | ||
- [Multiple Definitions For One Key](#multiple-definitions-for-one-key) | ||
- [Extending Schemas](#extending-schemas) | ||
- [Overriding When Extending](#overriding-when-extending) | ||
- [Subschemas](#subschemas) | ||
- [Extracting Schemas](#extracting-schemas) | ||
- [Schema Keys](#schema-keys) | ||
- [Schema Rules](#schema-rules) | ||
- [type](#type) | ||
- [label](#label) | ||
- [optional](#optional) | ||
- [min/max](#minmax) | ||
- [exclusiveMin/exclusiveMax](#exclusiveminexclusivemax) | ||
- [minCount/maxCount](#mincountmaxcount) | ||
- [allowedValues](#allowedvalues) | ||
- [regEx](#regex) | ||
- [blackbox](#blackbox) | ||
- [trim](#trim) | ||
- [custom](#custom) | ||
- [defaultValue](#defaultvalue) | ||
- [autoValue](#autovalue) | ||
- [Validating Data](#validating-data) | ||
- [The Object to Validate](#the-object-to-validate) | ||
- [Ways to Perform Validation](#ways-to-perform-validation) | ||
- [Named Validation Contexts](#named-validation-contexts) | ||
- [Unnamed Validation Contexts](#unnamed-validation-contexts) | ||
- [Validating an Object](#validating-an-object) | ||
- [Validating Only Some Keys in an Object](#validating-only-some-keys-in-an-object) | ||
- [Validation Options](#validation-options) | ||
- [Validating and Throwing ValidationErrors](#validating-and-throwing-validationerrors) | ||
- [Customize the Error That is Thrown](#customize-the-error-that-is-thrown) | ||
- [Custom Field Validation](#custom-field-validation) | ||
- [Custom Whole-Document Validators](#custom-whole-document-validators) | ||
- [Manually Adding a Validation Error](#manually-adding-a-validation-error) | ||
- [Asynchronous Custom Validation on the Client](#asynchronous-custom-validation-on-the-client) | ||
- [Getting a List of Invalid Keys and Validation Error Messages](#getting-a-list-of-invalid-keys-and-validation-error-messages) | ||
- [Customizing Validation Messages](#customizing-validation-messages) | ||
- [Other Validation Context Methods](#other-validation-context-methods) | ||
- [Other SimpleSchema Methods](#other-simpleschema-methods) | ||
- [Cleaning Objects](#cleaning-objects) | ||
- [Dates](#dates) | ||
- [Best Practice Code Examples](#best-practice-code-examples) | ||
- [Make a field conditionally required](#make-a-field-conditionally-required) | ||
- [Validate one key against another](#validate-one-key-against-another) | ||
- [Debug Mode](#debug-mode) | ||
- [Extending the Schema Options](#extending-the-schema-options) | ||
- [Add On Packages](#add-on-packages) | ||
- [License](#license) | ||
- [Contributing](#contributing) | ||
- [Thanks](#thanks) | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
## The History of SimpleSchema | ||
@@ -18,0 +91,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
139167
22
2091
1040