waterline
Advanced tools
Comparing version 0.10.0-rc6 to 0.10.0-rc7
@@ -5,12 +5,12 @@ /** | ||
var normalize = require('../utils/normalize'), | ||
schema = require('../utils/schema'), | ||
hasOwnProperty = require('../utils/helpers').object.hasOwnProperty; | ||
var normalize = require('../utils/normalize'); | ||
var schema = require('../utils/schema'); | ||
var hasOwnProperty = require('../utils/helpers').object.hasOwnProperty; | ||
/** | ||
* DQL Adapter Normalization | ||
*/ | ||
module.exports = { | ||
@@ -22,2 +22,13 @@ | ||
/** | ||
* join() | ||
* | ||
* If `join` is defined in the adapter, Waterline will use it to optimize | ||
* the `.populate()` implementation when joining collections within the same | ||
* database connection. | ||
* | ||
* @param {[type]} criteria | ||
* @param {Function} cb | ||
*/ | ||
join: function(criteria, cb) { | ||
@@ -30,3 +41,3 @@ | ||
// Build Default Error Message | ||
var err = "No join() method defined in adapter!"; | ||
var err = 'No join() method defined in adapter!'; | ||
@@ -48,4 +59,16 @@ // Find the connection to run this on | ||
/** | ||
* create() | ||
* | ||
* Create one or more models. | ||
* | ||
* @param {[type]} values [description] | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
*/ | ||
create: function(values, cb) { | ||
var globalId = this.query.globalId; | ||
// Normalize Arguments | ||
@@ -57,3 +80,3 @@ cb = normalize.callback(cb); | ||
// Build Default Error Message | ||
var err = "No create() method defined in adapter!"; | ||
var err = 'No create() method defined in adapter!'; | ||
@@ -67,6 +90,22 @@ // Find the connection to run this on | ||
if(!hasOwnProperty(adapter, 'create')) return cb(new Error(err)); | ||
adapter.create(connName, this.collection, values, cb); | ||
adapter.create(connName, this.collection, values, normalize.callback(function afterwards (err, createdRecord) { | ||
if (err) { | ||
if (typeof err === 'object') err.model = globalId; | ||
return cb(err); | ||
} | ||
else return cb(null, createdRecord); | ||
})); | ||
}, | ||
// Find a set of models | ||
/** | ||
* find() | ||
* | ||
* Find a set of models. | ||
* | ||
* @param {[type]} criteria [description] | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
*/ | ||
find: function(criteria, cb) { | ||
@@ -79,3 +118,3 @@ | ||
// Build Default Error Message | ||
var err = "No find() method defined in adapter!"; | ||
var err = 'No find() method defined in adapter!'; | ||
@@ -92,3 +131,12 @@ // Find the connection to run this on | ||
// Find exactly one model | ||
/** | ||
* findOne() | ||
* | ||
* Find exactly one model. | ||
* | ||
* @param {[type]} criteria [description] | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
*/ | ||
findOne: function(criteria, cb) { | ||
@@ -116,2 +164,8 @@ | ||
/** | ||
* [count description] | ||
* @param {[type]} criteria [description] | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
*/ | ||
count: function(criteria, cb) { | ||
@@ -147,4 +201,14 @@ var connName; | ||
/** | ||
* [update description] | ||
* @param {[type]} criteria [description] | ||
* @param {[type]} values [description] | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
*/ | ||
update: function (criteria, values, cb) { | ||
var globalId = this.query.globalId; | ||
// Normalize Arguments | ||
@@ -165,5 +229,18 @@ cb = normalize.callback(cb); | ||
adapter.update(connName, this.collection, criteria, values, cb); | ||
adapter.update(connName, this.collection, criteria, values, normalize.callback(function afterwards (err, updatedRecords) { | ||
if (err) { | ||
if (typeof err === 'object') err.model = globalId; | ||
return cb(err); | ||
} | ||
return cb(null, updatedRecords); | ||
})); | ||
}, | ||
/** | ||
* [destroy description] | ||
* @param {[type]} criteria [description] | ||
* @param {Function} cb [description] | ||
* @return {[type]} [description] | ||
*/ | ||
destroy: function(criteria, cb) { | ||
@@ -170,0 +247,0 @@ |
@@ -8,8 +8,12 @@ /** | ||
var _ = require('lodash'), | ||
anchor = require('anchor'), | ||
async = require('async'), | ||
utils = require('../utils/helpers'), | ||
hasOwnProperty = utils.object.hasOwnProperty; | ||
var _ = require('lodash'); | ||
var anchor = require('anchor'); | ||
var async = require('async'); | ||
var utils = require('../utils/helpers'); | ||
var hasOwnProperty = utils.object.hasOwnProperty; | ||
var WLValidationError = require('../error/WLValidationError'); | ||
/** | ||
@@ -65,6 +69,9 @@ * Build up validations using the Anchor module. | ||
// Ignore null values | ||
if(attrs[attr][prop] === null) return; | ||
// If property is reserved don't do anything with it | ||
if(['defaultsTo', 'primaryKey', 'autoIncrement', 'unique', 'index', 'collection', 'dominant', | ||
'columnName', 'foreignKey', 'references', 'on', 'groupKey', 'model', 'via', 'size', | ||
'example', 'validationMessage'].indexOf(prop) > -1) return; | ||
'example', 'validationMessage', 'validations'].indexOf(prop) > -1) return; | ||
@@ -172,8 +179,9 @@ // use the Anchor `in` method for enums | ||
// Validate all validations in parallell | ||
async.each(validations, validate, function() { | ||
// Validate all validations in parallel | ||
async.each(validations, validate, function allValidationsChecked () { | ||
if(Object.keys(errors).length === 0) return cb(); | ||
cb({ 'ValidationError': errors }); | ||
cb(errors); | ||
}); | ||
}; |
@@ -7,12 +7,30 @@ /** | ||
_ = require('lodash'), | ||
WLError = require('./WLError'), | ||
WLValidationError = require('./WLValidationError'), | ||
WLAdapterError = require('./WLAdapterError'), | ||
_isValidationError = require('./isValidationError'), | ||
_isConstraintViolation = require('./isConstraintViolation'), | ||
_isAdapterError = require('./isAdapterError'); | ||
WLError = require('./WLError'); | ||
WLValidationError = require('./WLValidationError'); | ||
// Expose a closure which returns the appropriate class of WLError | ||
module.exports = function(err) { | ||
/** | ||
* A classifier which normalizes a mystery error into a simple, | ||
* consistent format. This ensures that our instance which is | ||
* "new"-ed up belongs to one of a handful of distinct categories | ||
* and has a predictable method signature and properties. | ||
* | ||
* The returned error instance will always be or extend from | ||
* `WLError` (which extends from `Error`) | ||
* | ||
* NOTE: | ||
* This method should eventually be deprecated in a | ||
* future version of Waterline. It exists to help | ||
* w/ error type negotiation. In general, Waterline | ||
* should use WLError, or errors which extend from it | ||
* to construct error objects of the appropriate type. | ||
* In other words, no ** new ** errors should need to | ||
* be wrapped in a call to `errorify` - instead, code | ||
* necessary to handle any new error conditions should | ||
* construct a `WLError` directly and return that. | ||
* | ||
* @param {???} err | ||
* @return {WLError} | ||
*/ | ||
module.exports = function errorify(err) { | ||
@@ -22,3 +40,3 @@ // If specified `err` is already a WLError, just return it. | ||
return negotiate(err); | ||
return duckType(err); | ||
}; | ||
@@ -30,31 +48,55 @@ | ||
* Determine which type of error we're working with. | ||
* Err... using hacks. | ||
* | ||
* @return {[type]} [description] | ||
*/ | ||
function negotiate(err) { | ||
function duckType(err) { | ||
// Validation or constraint violation error (`E_VALIDATION`) | ||
// | ||
// | ||
// i.e. detected before talking to adapter, like `minLength` | ||
// i.e. constraint violation reported by adapter, like `unique` | ||
if (_isValidationError(err) || _isConstraintViolation(err)) { | ||
if (/*_isValidationError(err) || */_isConstraintViolation(err)) { | ||
// Dress `unique` rule violations to be consistent with other | ||
// validation errors. | ||
return new WLValidationError(err); | ||
} | ||
// Adapter error (`E_ADAPTER`) | ||
// | ||
// Miscellaneous physical-layer consistency violation | ||
// i.e. reported by adapter via `waterline-errors` | ||
else if (_isAdapterError(err)) { | ||
return new WLAdapterError(err); | ||
} | ||
// Unexpected miscellaneous error (`E_UNKNOWN`) | ||
// | ||
// | ||
// (i.e. helmet fire. The database crashed or something. Or there's an adapter | ||
// bug. Or a bug in WL core.) | ||
else { | ||
return new WLError(err); | ||
return new WLError({ | ||
originalError: err | ||
}); | ||
} | ||
/** | ||
* @param {?} err | ||
* @return {Boolean} whether this is an adapter-level constraint | ||
* violation (e.g. `unique`) | ||
*/ | ||
function _isConstraintViolation(err) { | ||
// If a proper error code is specified, this error can be classified. | ||
if (err && typeof err === 'object' && err.code === 'E_UNIQUE') { | ||
return true; | ||
} | ||
// Otherwise, there is not enough information to call this a | ||
// constraint violation error and provide proper explanation to | ||
// the architect. | ||
else return false; | ||
} | ||
// /** | ||
// * @param {?} err | ||
// * @return {Boolean} whether this is a validation error (e.g. minLength exceeded for attribute) | ||
// */ | ||
// function _isValidationError(err) { | ||
// return _.isObject(err) && err.ValidationError; | ||
// } | ||
@@ -6,67 +6,162 @@ /** | ||
var util = require('util') | ||
, http = require('http') | ||
, _ = require('lodash'); | ||
module.exports = WLError; | ||
/** | ||
* WLError | ||
* | ||
* A classifier which normalizes a mystery error into a simple, | ||
* consistent format. WLError ensures that the instance that is | ||
* "new"-ed up belongs to one of a handful of distinct categories | ||
* and has a predictable method signature and properties. | ||
* All errors passed to a query callback in Waterline extend | ||
* from this base error class. | ||
* | ||
* @param {?} err | ||
* @param {Object} properties | ||
* @constructor {WLError} | ||
*/ | ||
function WLError(err) { | ||
function WLError( properties ) { | ||
// Save reference to original error. | ||
this.originalError = err; | ||
// Call super constructor (Error) | ||
WLError.super_.call(this); | ||
// Default properties | ||
this.status = 'error'; | ||
this.code = 'E_UNKNOWN'; | ||
this.msg = 'Waterline: ORM encountered an unknown error.'; | ||
// Fold defined properties into the new WLError instance. | ||
properties = properties||{}; | ||
_.extend(this, properties); | ||
} | ||
util.inherits(WLError, Error); | ||
// Default properties | ||
WLError.prototype.status = | ||
500; | ||
WLError.prototype.code = | ||
'E_UNKNOWN'; | ||
WLError.prototype.reason = | ||
'Encountered an unexpected error'; | ||
/** | ||
* Override JSON serialization. | ||
* (i.e. when this error is passed to `res.json()` or `JSON.stringify`) | ||
* | ||
* For example: | ||
* ```json | ||
* { | ||
* status: 500, | ||
* code: 'E_UNKNOWN' | ||
* } | ||
* ``` | ||
* | ||
* @return {Object} | ||
*/ | ||
WLError.prototype.toJSON = WLError.prototype.toPOJO = | ||
function toPOJO() { | ||
WLError.prototype.toJSON = | ||
WLError.prototype.toPOJO = | ||
function () { | ||
var obj = { | ||
error: this.code, | ||
summary: this.reason, | ||
status: this.status, | ||
raw: this.explain() | ||
}; | ||
// Worst case, try to dress up the original error as much as possible. | ||
return { | ||
status: this.status, | ||
message: this.msg, | ||
// TODO: make this better (i.e. not a hard-coded check-- use the inheritance approach discussed in TODO at top of this file and override the toJSON() function for the validation error case) | ||
details: this.invalidAttributes || this.toString(), | ||
code: this.code | ||
}; | ||
// Only include `raw` if its truthy. | ||
if (!obj.raw) delete obj.raw; | ||
return obj; | ||
}; | ||
/** | ||
* Override output for `sails.log[.*]` | ||
* | ||
* @return {String} | ||
* | ||
* For example: | ||
* ```sh | ||
* Waterline: ORM encountered an unexpected error: | ||
* { ValidationError: { name: [ [Object], [Object] ] } } | ||
* ``` | ||
*/ | ||
WLError.prototype.toLog = function () { | ||
return this.inspect(); | ||
}; | ||
/** | ||
* Override output for `util.inspect` | ||
* (also when this error is logged using `console.log`) | ||
* | ||
* @return {String} | ||
*/ | ||
WLError.prototype.inspect = function () { | ||
return util.format('Error (%s)', this.code) + ' :: ' + /*this.toString() +*/ '\n'+util.inspect(this.toPOJO()); | ||
}; | ||
/** | ||
* @return {String} | ||
*/ | ||
WLError.prototype.toString = function () { | ||
var output = ''; | ||
var summary = this.summarize(); | ||
var explanation = this.explain(); | ||
// Try to dress up the original error as much as possible. | ||
var stringifiedErr; | ||
if (_.isString(this.originalError)) { | ||
stringifiedErr = this.originalError; | ||
} | ||
// Run toString() on Errors | ||
else if (_.isObject(this.originalError) && this.originalError instanceof Error && _.isFunction(this.originalError.toString) ) { | ||
stringifiedErr = this.originalError.toString(); | ||
} | ||
// But for other objects, use util.inspect() | ||
else { | ||
stringifiedErr = util.inspect(this.originalError); | ||
} | ||
return stringifiedErr; | ||
output += summary; | ||
output += (summary && explanation) ? ':' : '.'; | ||
if (explanation) { | ||
output += '\n' + explanation; | ||
} | ||
return output; | ||
}; | ||
/** | ||
* @return {String} | ||
*/ | ||
WLError.prototype.summarize = function () { | ||
var output = ''; | ||
if (this.reason) { | ||
output += this.reason; | ||
} | ||
else { | ||
output += this.getCanonicalStatusMessage(); | ||
} | ||
return output; | ||
}; | ||
/** | ||
* @return {String} a detailed explanation of this error | ||
*/ | ||
WLError.prototype.explain = function () { | ||
// Try to dress up the wrapped "original" error as much as possible. | ||
if (!this.originalError) { | ||
return ''; | ||
} | ||
else if (typeof this.originalError === 'string') { | ||
return this.originalError; | ||
} | ||
// Run toString() on Errors | ||
else if ( util.isError(this.originalError) ) { | ||
return this.originalError.toString(); | ||
} | ||
// But for other objects, use util.inspect() | ||
else { | ||
return util.inspect(this.originalError); | ||
} | ||
}; | ||
/** | ||
* @return {String} description of this error's status code, as defined by HTTP. | ||
*/ | ||
WLError.prototype.getCanonicalStatusMessage = function () { | ||
return http.STATUS_CODES[this.status]; | ||
}; | ||
module.exports = WLError; |
@@ -6,10 +6,8 @@ /** | ||
var WLError = require('./WLError'); | ||
var nodeutil = require('util'); | ||
var WLUsageError = require('./WLUsageError'); | ||
var util = require('util'); | ||
var _ = require('lodash'); | ||
module.exports = WLValidationError; | ||
/** | ||
@@ -20,15 +18,168 @@ * WLValidationError | ||
*/ | ||
function WLValidationError (err) { | ||
function WLValidationError (properties) { | ||
// Call super | ||
WLError.call(this, err); | ||
// Call superclass | ||
WLValidationError.super_.call(this, properties); | ||
// Override default WLError properties | ||
this.status = 'invalid'; | ||
// Ensure valid usage | ||
if ( typeof this.invalidAttributes !== 'object' ) { | ||
return new WLUsageError({ | ||
reason: 'An `invalidAttributes` object must be passed into the constructor for `WLValidationError`' | ||
}); | ||
} | ||
// if ( typeof this.model !== 'string' ) { | ||
// return new WLUsageError({ | ||
// reason: 'A `model` string (the collection\'s `globalId`) must be passed into the constructor for `WLValidationError`' | ||
// }); | ||
// } | ||
// Customize the `reason` based on the # of invalid attributes | ||
// (`reason` may not be overridden) | ||
var isSingular = this.length === 1; | ||
this.reason = util.format('%d attribute%s %s invalid', | ||
this.length, | ||
isSingular ? '' : 's', | ||
isSingular ? 'is' : 'are'); | ||
// Always apply the 'E_VALIDATION' error code, even if it was overridden. | ||
this.code = 'E_VALIDATION'; | ||
this.msg = 'Waterline: Could not complete operation - violates one or more of your model\'s validation rules.'; | ||
this.invalidAttributes = this.originalError.ValidationError; | ||
// Status may be overridden. | ||
this.status = properties.status || 400; | ||
// Model should always be set. | ||
// (this should be the globalId of model, or "collection") | ||
this.model = properties.model; | ||
// Ensure messages exist for each invalidAttribute | ||
this.invalidAttributes = _.mapValues(this.invalidAttributes, function (rules, attrName) { | ||
return _.map(rules, function (rule) { | ||
if (!rule.message) { | ||
rule.message = util.format('A record with that `%s` already exists (`%s`).', attrName, rule.value); | ||
} | ||
return rule; | ||
}); | ||
}); | ||
} | ||
nodeutil.inherits(WLValidationError, WLError); | ||
util.inherits(WLValidationError, WLError); | ||
/** | ||
* `rules` | ||
* | ||
* @return {Object[Array[String]]} dictionary of validation rule ids, indexed by attribute | ||
*/ | ||
WLValidationError.prototype.__defineGetter__('rules', function(){ | ||
return _.mapValues(this.invalidAttributes, function (rules, attrName) { | ||
return _.pluck(rules, 'rule'); | ||
}); | ||
}); | ||
/** | ||
* `messages` (aka `errors`) | ||
* | ||
* @return {Object[Array[String]]} dictionary of validation messages, indexed by attribute | ||
*/ | ||
WLValidationError.prototype.__defineGetter__('messages', function(){ | ||
return _.mapValues(this.invalidAttributes, function (rules, attrName) { | ||
return _.pluck(rules, 'message'); | ||
}); | ||
}); | ||
WLValidationError.prototype.__defineGetter__('errors', function(){ | ||
return this.messages; | ||
}); | ||
/** | ||
* `attributes` (aka `keys`) | ||
* | ||
* @return {Array[String]} of invalid attribute names | ||
*/ | ||
WLValidationError.prototype.__defineGetter__('attributes', function(){ | ||
return _.keys(this.invalidAttributes); | ||
}); | ||
WLValidationError.prototype.__defineGetter__('keys', function(){ | ||
return this.attributes; | ||
}); | ||
/** | ||
* `.length` | ||
* | ||
* @return {Integer} number of invalid attributes | ||
*/ | ||
WLValidationError.prototype.__defineGetter__('length', function(){ | ||
return this.attributes.length; | ||
}); | ||
/** | ||
* `.ValidationError` | ||
* (backwards-compatibility) | ||
* | ||
* @return {Object[Array[Object]]} number of invalid attributes | ||
*/ | ||
WLValidationError.prototype.__defineGetter__('ValidationError', function(){ | ||
// | ||
// TODO: | ||
// Down the road- emit deprecation event here-- | ||
// (will log information about new error handling options) | ||
// | ||
return this.invalidAttributes; | ||
}); | ||
/** | ||
* [toJSON description] | ||
* @type {[type]} | ||
*/ | ||
WLValidationError.prototype.toJSON = | ||
WLValidationError.prototype.toPOJO = | ||
function () { | ||
return { | ||
error: this.code, | ||
model: this.model, | ||
summary: this.reason, | ||
status: this.status, | ||
invalidAttributes: this.invalidAttributes | ||
}; | ||
}; | ||
/** | ||
* @return {String} a detailed explanation of this error | ||
*/ | ||
WLValidationError.prototype.explain = function () { | ||
return _.reduce(this.messages, function (memo, messages, attrName) { | ||
memo += attrName + ':\n'; | ||
memo += _.reduce(messages, function (memo, message) { | ||
memo += ' ' + message + '\n'; | ||
return memo; | ||
}, ''); | ||
return memo; | ||
}, ''); | ||
}; | ||
/** | ||
* Override output for `util.inspect` | ||
* (also when this error is logged using `console.log`) | ||
* | ||
* @return {String} | ||
*/ | ||
WLError.prototype.inspect = function () { | ||
return util.format('Error (%s)', this.code) + ' :: ' + this.toString() + '\nDetails:\n'+util.inspect(this.toPOJO()); | ||
}; | ||
module.exports = WLValidationError; |
@@ -68,2 +68,3 @@ | ||
values.forEach(function(record) { | ||
if(record === null) return; | ||
var item = Object.create(record.__proto__); | ||
@@ -70,0 +71,0 @@ Object.keys(record).forEach(function(key) { |
@@ -95,3 +95,5 @@ /** | ||
self.adapter.create(values, function(err, values) { | ||
if(err) return cb(err); | ||
if(err) { | ||
return cb(err); | ||
} | ||
@@ -98,0 +100,0 @@ // Unserialize values |
@@ -15,3 +15,4 @@ /** | ||
_ = require('lodash'), | ||
async = require('async'); | ||
async = require('async'), | ||
hasOwnProperty = utils.object.hasOwnProperty; | ||
@@ -91,4 +92,17 @@ module.exports = { | ||
// We need to run one last check on the results using the criteria. This allows a self | ||
// association where we end up with two records in the cache both having each other as | ||
// embedded objects and we only want one result. However we need to filter any join criteria | ||
// out of the top level where query so that searchs by primary key still work. | ||
var tmpCriteria = _.cloneDeep(criteria.where); | ||
if(!tmpCriteria) tmpCriteria = {}; | ||
criteria.joins.forEach(function(join) { | ||
if(!hasOwnProperty(join, 'alias')) return; | ||
if(!hasOwnProperty(tmpCriteria, join.alias)) return; | ||
delete tmpCriteria[join.alias]; | ||
}); | ||
// Pass results into Waterline-Criteria | ||
var _criteria = { where: _.cloneDeep(criteria.where) }; | ||
var _criteria = { where: tmpCriteria }; | ||
results = waterlineCriteria('parent', { parent: results }, _criteria).results; | ||
@@ -119,3 +133,3 @@ | ||
if(!results) return cb() | ||
if(!results) return cb(); | ||
@@ -243,4 +257,17 @@ // Normalize results to an array | ||
// We need to run one last check on the results using the criteria. This allows a self | ||
// association where we end up with two records in the cache both having each other as | ||
// embedded objects and we only want one result. However we need to filter any join criteria | ||
// out of the top level where query so that searchs by primary key still work. | ||
var tmpCriteria = _.cloneDeep(criteria.where); | ||
if(!tmpCriteria) tmpCriteria = {}; | ||
criteria.joins.forEach(function(join) { | ||
if(!hasOwnProperty(join, 'alias')) return; | ||
if(!hasOwnProperty(tmpCriteria, join.alias)) return; | ||
delete tmpCriteria[join.alias]; | ||
}); | ||
// Pass results into Waterline-Criteria | ||
var _criteria = { where: _.cloneDeep(criteria.where) }; | ||
var _criteria = { where: tmpCriteria }; | ||
results = waterlineCriteria('parent', { parent: results }, _criteria).results; | ||
@@ -271,2 +298,4 @@ | ||
if(!results) return cb(null, []); | ||
// Normalize results to an array | ||
@@ -273,0 +302,0 @@ if(!Array.isArray(results) && results) results = [results]; |
@@ -478,4 +478,4 @@ | ||
_tmpCriteria = _.cloneDeep(userCriteria); | ||
_tmpCriteria = normalize.criteria(_tmpCriteria); | ||
// Ensure `where` criteria is properly formatted | ||
@@ -667,3 +667,3 @@ if(hasOwnProperty(userCriteria, 'where')) { | ||
delete userCriteria.sort; | ||
criteria = _.merge(userCriteria, criteria); | ||
criteria = _.extend(criteria, userCriteria); | ||
} | ||
@@ -670,0 +670,0 @@ |
@@ -40,4 +40,11 @@ /** | ||
function(cb) { | ||
self._validator.validate(values, presentOnly === true, function(err) { | ||
if(err) return cb(err); | ||
self._validator.validate(values, presentOnly === true, function(invalidAttributes) { | ||
// Create validation error here | ||
// (pass in the invalid attributes as well as the collection's globalId) | ||
if(invalidAttributes) return cb(new WLValidationError({ | ||
invalidAttributes: invalidAttributes, | ||
model: self.globalId | ||
})); | ||
cb(); | ||
@@ -44,0 +51,0 @@ }); |
@@ -82,4 +82,8 @@ | ||
exports.matchMongoId = function matchMongoId(id) { | ||
if(typeof id.toString === 'undefined') return false; | ||
return id.toString().match(/^[a-fA-F0-9]{24}$/) ? true : false; | ||
// id must be truthy- and either BE a string, or be an object | ||
// with a toString method. | ||
if( !id || | ||
! (_.isString(id) || (_.isObject(id) || _.isFunction(id.toString))) | ||
) return false; | ||
else return id.toString().match(/^[a-fA-F0-9]{24}$/) ? true : false; | ||
}; |
@@ -58,3 +58,3 @@ /** | ||
// Ensure type is lower case | ||
if(typeof attributes[key].type !== 'undefined') { | ||
if(attributes[key].type && typeof attributes[key].type !== 'undefined') { | ||
attributes[key].type = attributes[key].type.toLowerCase(); | ||
@@ -61,0 +61,0 @@ } |
{ | ||
"name": "waterline", | ||
"description": "An ORM for Node.js and the Sails framework", | ||
"version": "0.10.0-rc6", | ||
"version": "0.10.0-rc7", | ||
"homepage": "http://github.com/balderdashy/waterline", | ||
@@ -25,3 +25,3 @@ "contributors": [ | ||
"q": "~0.9.7", | ||
"waterline-schema": "~0.1.6", | ||
"waterline-schema": "~0.1.7", | ||
"node-switchback": "~0.0.4", | ||
@@ -28,0 +28,0 @@ "waterline-criteria": "~0.10.3" |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
284699
7662
87
1
Updatedwaterline-schema@~0.1.7