waterline
Advanced tools
Comparing version 0.10.0-rc5 to 0.10.0-rc6
@@ -164,8 +164,5 @@ var _ = require('lodash'), | ||
schemas[collection.identity] = { | ||
definition: schema, | ||
identity: collection.tableName || collection.identity, | ||
meta: meta | ||
}; | ||
schemas[collection.identity] = collection; | ||
schemas[collection.identity].definition = schema; | ||
schemas[collection.identity].meta = meta; | ||
}); | ||
@@ -172,0 +169,0 @@ |
@@ -61,7 +61,2 @@ /** | ||
// Check if a default connection was specified | ||
if(hasOwnProperty(this.defaults, 'connection')) { | ||
collection.prototype.connection = this.defaults.connection; | ||
} | ||
// Find the connections used by this collection | ||
@@ -68,0 +63,0 @@ // If none is specified check if a default connection exist |
@@ -66,3 +66,4 @@ /** | ||
if(['defaultsTo', 'primaryKey', 'autoIncrement', 'unique', 'index', 'collection', 'dominant', | ||
'columnName', 'foreignKey', 'references', 'on', 'groupKey', 'model', 'via', 'size'].indexOf(prop) > -1) return; | ||
'columnName', 'foreignKey', 'references', 'on', 'groupKey', 'model', 'via', 'size', | ||
'example', 'validationMessage'].indexOf(prop) > -1) return; | ||
@@ -69,0 +70,0 @@ // use the Anchor `in` method for enums |
@@ -5,60 +5,35 @@ /** | ||
var util = require('util') | ||
, _ = require('lodash') | ||
, ERRORTYPES = require('./types') | ||
, _isValidationError = require('./isValidationError') | ||
, _isConstraintViolation = require('./isConstraintViolation') | ||
, _isAdapterError = require('./isAdapterError'); | ||
var util = require('util'), | ||
_ = require('lodash'), | ||
WLError = require('./WLError'), | ||
WLValidationError = require('./WLValidationError'), | ||
WLAdapterError = require('./WLAdapterError'), | ||
_isValidationError = require('./isValidationError'), | ||
_isConstraintViolation = require('./isConstraintViolation'), | ||
_isAdapterError = require('./isAdapterError'); | ||
// Expose a closure which returns the appropriate class of WLError | ||
module.exports = function(err) { | ||
// Expose WLError constructor | ||
module.exports = WLError; | ||
// If specified `err` is already a WLError, just return it. | ||
if (typeof err === 'object' && err instanceof WLError) return err; | ||
return negotiate(err); | ||
}; | ||
// TODO: | ||
// This problem could be more cleanly solved by subclassing WLError | ||
// into WLInvalidError, WLConstraintError, WLAdapterError, but that | ||
// can be done in a future refactoring. The usage can remain consistent | ||
// regardless, so backwards compatiblity is ensured, since users will | ||
// need to ducktype errors using type/code/status properties. | ||
/** | ||
* 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. | ||
* | ||
* @param {?} err | ||
* @constructor {WLError} | ||
* Determine which type of error we're working with. | ||
* @return {[type]} [description] | ||
*/ | ||
function WLError(err) { | ||
function negotiate(err) { | ||
// If specified `err` is already a WLError, just return it. | ||
if (typeof err === 'object' && err instanceof WLError) return err; | ||
// Save reference to original error. | ||
this.originalError = err; | ||
// Logical validation error (`E_VALIDATION`) | ||
// Validation or constraint violation error (`E_VALIDATION`) | ||
// | ||
// i.e. detected before talking to adapter, like `minLength` | ||
if ( _isValidationError(this.originalError) ) { | ||
this.status = ERRORTYPES.E_VALIDATION.status; | ||
this.code = ERRORTYPES.E_VALIDATION.code; | ||
this.msg = ERRORTYPES.E_VALIDATION.msg; | ||
this.invalidAttributes = this.originalError.ValidationError; | ||
} | ||
// Constraint validation error (`E_CONSTRAINT`) | ||
// | ||
// i.e. constraint violation reported by adapter, like `unique` | ||
else if ( _isConstraintViolation(this.originalError) ) { | ||
this.status = ERRORTYPES.E_CONSTRAINT.status; | ||
this.code = ERRORTYPES.E_CONSTRAINT.code; | ||
this.msg = ERRORTYPES.E_CONSTRAINT.msg; | ||
if (_isValidationError(err) || _isConstraintViolation(err)) { | ||
return new WLValidationError(err); | ||
} | ||
@@ -70,6 +45,4 @@ | ||
// i.e. reported by adapter via `waterline-errors` | ||
else if ( _isAdapterError(this.originalError) ) { | ||
this.status = ERRORTYPES.E_ADAPTER.status; | ||
this.code = ERRORTYPES.E_ADAPTER.code; | ||
this.msg = ERRORTYPES.E_ADAPTER.msg; | ||
else if (_isAdapterError(err)) { | ||
return new WLAdapterError(err); | ||
@@ -83,60 +56,5 @@ } | ||
else { | ||
this.status = ERRORTYPES.E_UNKNOWN.status; | ||
this.code = ERRORTYPES.E_UNKNOWN.code; | ||
this.msg = ERRORTYPES.E_UNKNOWN.msg; | ||
return new WLError(err); | ||
} | ||
} | ||
/** | ||
* @return {Object} | ||
*/ | ||
WLError.prototype.toJSON = WLError.prototype.toPOJO = | ||
function toPOJO() { | ||
// Best case, if we know the type of the original error, provide | ||
// a sexier toString() message. | ||
if (this.code !== 'E_UNKNOWN') { | ||
// TODO: actually write this part | ||
// return '????'; | ||
} | ||
// Worst case, try to dress up the original error as much as possible. | ||
return { | ||
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 | ||
}; | ||
}; | ||
/** | ||
* @return {String} | ||
*/ | ||
WLError.prototype.toString = function () { | ||
// Best case, if we know the type of the original error, provide | ||
// a sexier toString() message. | ||
if (this.code !== 'E_UNKNOWN') { | ||
// TODO: actually write this part | ||
// return '????'; | ||
} | ||
// Worst case, 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; | ||
}; |
@@ -10,3 +10,4 @@ /** | ||
* @param {?} err | ||
* @return {Boolean} whether this is a constraint violation (e.g. `unique`) | ||
* @return {Boolean} whether this is an adapter-level constraint | ||
* violation (e.g. `unique`) | ||
*/ | ||
@@ -13,0 +14,0 @@ module.exports = function isConstraintViolation(err) { |
@@ -33,3 +33,3 @@ /** | ||
* circular pattern is detected.) | ||
* | ||
* | ||
* @param {String} keyName [the initial alias aka named relation] | ||
@@ -130,2 +130,16 @@ * @param {Object} criteria [optional] | ||
// Build select object to use in the integrator | ||
var select = []; | ||
Object.keys(this._context.waterline.schema[attr.references].attributes).forEach(function(key) { | ||
var obj = self._context.waterline.schema[attr.references].attributes[key]; | ||
if(!hasOwnProperty(obj, 'columnName')) { | ||
select.push(key); | ||
return; | ||
} | ||
select.push(obj.columnName); | ||
}); | ||
join.select = select; | ||
// If linking to a junction table the attributes shouldn't be included in the return value | ||
@@ -132,0 +146,0 @@ if(this._context.waterline.schema[attr.references].junctionTable) join.select = false; |
@@ -13,2 +13,3 @@ /** | ||
Integrator = require('../integrator'), | ||
waterlineCriteria = require('waterline-criteria'), | ||
_ = require('lodash'), | ||
@@ -90,2 +91,6 @@ async = require('async'); | ||
// Pass results into Waterline-Criteria | ||
var _criteria = { where: _.cloneDeep(criteria.where) }; | ||
results = waterlineCriteria('parent', { parent: results }, _criteria).results; | ||
// Serialize values coming from an in-memory join before modelizing | ||
@@ -113,5 +118,17 @@ var _results = []; | ||
function returnResults(results) { | ||
if(!results) return cb() | ||
// Normalize results to an array | ||
if(!Array.isArray(results) && results) results = [results]; | ||
// Unserialize each of the results before attempting any join logic on them | ||
var unserializedModels = []; | ||
results.forEach(function(result) { | ||
unserializedModels.push(self._transformer.unserialize(result)); | ||
}); | ||
var models = []; | ||
var joins = criteria.joins ? criteria.joins : []; | ||
var data = new Joins(joins, results, self._schema.schema, self.waterline.collections); | ||
var data = new Joins(joins, unserializedModels, self._schema.schema, self.waterline.collections); | ||
@@ -123,9 +140,5 @@ // If `data.models` is invalid (not an array) return early to avoid getting into trouble. | ||
// Create a model for the top level values | ||
data.models.forEach(function(model) { | ||
// Unserialize result value | ||
var value = self._transformer.unserialize(model); | ||
// Create a model for the top level values | ||
models.push(new self._model(value, data.options)); | ||
models.push(new self._model(model, data.options)); | ||
}); | ||
@@ -231,2 +244,6 @@ | ||
// Pass results into Waterline-Criteria | ||
var _criteria = { where: _.cloneDeep(criteria.where) }; | ||
results = waterlineCriteria('parent', { parent: results }, _criteria).results; | ||
// Serialize values coming from an in-memory join before modelizing | ||
@@ -255,5 +272,17 @@ var _results = []; | ||
// Normalize results to an array | ||
if(!Array.isArray(results) && results) results = [results]; | ||
// Unserialize each of the results before attempting any join logic on them | ||
var unserializedModels = []; | ||
if(results) { | ||
results.forEach(function(result) { | ||
unserializedModels.push(self._transformer.unserialize(result)); | ||
}); | ||
} | ||
var models = []; | ||
var joins = criteria.joins ? criteria.joins : []; | ||
var data = new Joins(joins, results, self._schema.schema, self.waterline.collections); | ||
var data = new Joins(joins, unserializedModels, self._schema.schema, self.waterline.collections); | ||
@@ -265,12 +294,7 @@ // If `data.models` is invalid (not an array) return early to avoid getting into trouble. | ||
// Create a model for the top level values | ||
data.models.forEach(function(model) { | ||
// Unserialize result value | ||
var value = self._transformer.unserialize(model); | ||
// Create a model for the top level values | ||
models.push(new self._model(value, data.options)); | ||
models.push(new self._model(model, data.options)); | ||
}); | ||
cb(null, models); | ||
@@ -277,0 +301,0 @@ } |
@@ -121,3 +121,2 @@ /** | ||
Joins.prototype.modelize = function modelize(value) { | ||
var self = this; | ||
@@ -163,3 +162,4 @@ | ||
value[key].forEach(function(val) { | ||
var model; | ||
var collection, | ||
model; | ||
@@ -169,3 +169,5 @@ // If there is a joinKey this means it's a belongsTo association so the collection | ||
if(joinKey) { | ||
model = new self.collections[joinKey]._model(val, { showJoins: false }); | ||
collection = self.collections[joinKey]; | ||
val = collection._transformer.unserialize(val); | ||
model = new collection._model(val, { showJoins: false }); | ||
return records.push(model); | ||
@@ -176,3 +178,5 @@ } | ||
// the proper model from the collections. | ||
model = new self.collections[usedInJoin.join.child]._model(val, { showJoins: false }); | ||
collection = self.collections[usedInJoin.join.child]; | ||
val = collection._transformer.unserialize(val); | ||
model = new collection._model(val, { showJoins: false }); | ||
return records.push(model); | ||
@@ -188,3 +192,5 @@ }); | ||
// it directly on the attribute | ||
value[key] = new self.collections[joinKey]._model(value[key], { showJoins: false }); | ||
collection = self.collections[joinKey]; | ||
value[key] = collection._transformer.unserialize(value[key]); | ||
value[key] = new collection._model(value[key], { showJoins: false }); | ||
}); | ||
@@ -191,0 +197,0 @@ |
@@ -445,2 +445,3 @@ | ||
var self = this; | ||
var opts = []; | ||
@@ -472,6 +473,30 @@ | ||
var _tmpCriteria = {}; | ||
// Check if the join contains any criteria | ||
if(item.join.criteria) { | ||
var userCriteria = _.cloneDeep(item.join.criteria); | ||
delete userCriteria.sort; | ||
_tmpCriteria = _.cloneDeep(userCriteria); | ||
// Ensure `where` criteria is properly formatted | ||
if(hasOwnProperty(userCriteria, 'where')) { | ||
if(userCriteria.where === undefined) { | ||
delete userCriteria.where; | ||
} | ||
else { | ||
// If an array of primary keys was passed in, normalize the criteria | ||
if(Array.isArray(userCriteria.where)) { | ||
var pk = self.context.waterline.collections[item.join.child].primaryKey; | ||
var obj = {}; | ||
obj[pk] = _.clone(userCriteria.where); | ||
userCriteria.where = obj; | ||
} | ||
userCriteria = userCriteria.where; | ||
} | ||
} | ||
criteria = _.merge(userCriteria, criteria); | ||
@@ -481,6 +506,6 @@ } | ||
// Normalize criteria | ||
criteria = normalize.criteria({ where: criteria }); | ||
criteria = normalize.criteria(criteria); | ||
// If criteria contains a skip or limit option, an operation will be needed for each parent. | ||
if(hasOwnProperty(criteria, 'skip') || hasOwnProperty(criteria, 'limit')) { | ||
if(hasOwnProperty(_tmpCriteria, 'skip') || hasOwnProperty(_tmpCriteria, 'limit')) { | ||
parents.forEach(function(parent) { | ||
@@ -491,2 +516,6 @@ | ||
// Mixin the user defined skip and limit | ||
if(hasOwnProperty(_tmpCriteria, 'skip')) tmpCriteria.skip = _tmpCriteria.skip; | ||
if(hasOwnProperty(_tmpCriteria, 'limit')) tmpCriteria.limit = _tmpCriteria.limit; | ||
// Build a simple operation to run with criteria from the parent results. | ||
@@ -629,2 +658,14 @@ // Give it an ID so that children operations can reference it if needed. | ||
var userCriteria = _.cloneDeep(opt.join.criteria); | ||
// Ensure `where` criteria is properly formatted | ||
if(hasOwnProperty(userCriteria, 'where')) { | ||
if(userCriteria.where === undefined) { | ||
delete userCriteria.where; | ||
} | ||
else { | ||
userCriteria = userCriteria.where; | ||
} | ||
} | ||
delete userCriteria.sort; | ||
criteria = _.merge(userCriteria, criteria); | ||
@@ -631,0 +672,0 @@ } |
@@ -59,3 +59,3 @@ /** | ||
// to an instance in the REAL child collection. | ||
if (!childRow[childNamespace + childPK]) return memo; | ||
if (!childRow[childNamespace + childPK] && !childRow[childPK]) return memo; | ||
@@ -62,0 +62,0 @@ // Rename childRow's [fkToChild] key to [childPK] |
@@ -136,3 +136,3 @@ /** | ||
var fn = function(item, next) { | ||
item(next); | ||
item(values, next); | ||
}; | ||
@@ -139,0 +139,0 @@ |
@@ -5,3 +5,3 @@ var _ = require('lodash'); | ||
var switchback = require('node-switchback'); | ||
var WLError = require('../error'); | ||
var errorify = require('../error'); | ||
@@ -54,2 +54,6 @@ var normalize = module.exports = { | ||
// Let the calling method normalize array criteria. It could be an IN query | ||
// where we need the PK of the collection or a .findOrCreateEach | ||
if(Array.isArray(criteria)) return criteria; | ||
// Empty undefined values from criteria object | ||
@@ -68,2 +72,6 @@ _.each(criteria, function(val, key) { | ||
if(_.isObject(criteria) && !criteria.where && criteria.where !== null) { | ||
criteria = { where: criteria }; | ||
} | ||
// Return string to indicate an error | ||
@@ -91,2 +99,44 @@ if(!_.isObject(criteria)) throw new Error('Invalid options/criteria :: ' + criteria); | ||
// Move Limit, Skip, sort outside the where criteria | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'limit')) { | ||
criteria.limit = _.clone(criteria.where.limit); | ||
delete criteria.where.limit; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'skip')) { | ||
criteria.skip = _.clone(criteria.where.skip); | ||
delete criteria.where.skip; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'sort')) { | ||
criteria.sort = _.clone(criteria.where.sort); | ||
delete criteria.where.sort; | ||
} | ||
// Pull out aggregation keys from where key | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'sum')) { | ||
criteria.sum = _.clone(criteria.where.sum); | ||
delete criteria.where.sum; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'average')) { | ||
criteria.average = _.clone(criteria.where.average); | ||
delete criteria.where.average; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'groupBy')) { | ||
criteria.groupBy = _.clone(criteria.where.groupBy); | ||
delete criteria.where.groupBy; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'min')) { | ||
criteria.min = _.clone(criteria.where.min); | ||
delete criteria.where.min; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'max')) { | ||
criteria.max = _.clone(criteria.where.max); | ||
delete criteria.where.max; | ||
} | ||
// If WHERE is {}, always change it back to null | ||
@@ -171,13 +221,2 @@ if(criteria.where && _.keys(criteria.where).length === 0) { | ||
// Move Limit and Skip outside the where criteria | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'limit')) { | ||
delete criteria.where.limit; | ||
criteria.limit = origCriteria.where.limit; | ||
} | ||
if(hop(criteria, 'where') && criteria.where !== null && hop(criteria.where, 'skip')) { | ||
delete criteria.where.skip; | ||
criteria.skip = origCriteria.where.skip; | ||
} | ||
return criteria; | ||
@@ -235,3 +274,3 @@ }, | ||
wrappedCallback = function (err) { | ||
// If no error occurred, immediately trigger the original callback | ||
@@ -245,3 +284,3 @@ // without messing up the context or arguments: | ||
// (if it isn't one already) | ||
err = new WLError(err); | ||
err = errorify(err); | ||
@@ -262,7 +301,7 @@ var modifiedArgs = Array.prototype.slice.call(arguments,1); | ||
// TODO: Make it clear that switchback support it experimental. | ||
// | ||
// | ||
// Push switchback support off until >= v0.11 | ||
// or at least add a warning about it being a `stage 1: experimental` | ||
// feature. | ||
// | ||
// | ||
@@ -269,0 +308,0 @@ if (!_.isFunction(cb)) wrappedCallback = cb; |
@@ -151,5 +151,3 @@ /** | ||
function defaultFn(fn) { | ||
return fn === 'afterDestroy' ? | ||
function(next) { return next(); } : | ||
function(values, next) { return next(); }; | ||
return function(values, next) { return next(); }; | ||
} | ||
@@ -156,0 +154,0 @@ |
@@ -49,4 +49,4 @@ /** | ||
_.each(_.keys(sortCriteria), function(key) { | ||
if(sortCriteria[key] === -1) sortArray.push('-' + key.toLowerCase()); | ||
else sortArray.push(key.toLowerCase()); | ||
if(sortCriteria[key] === -1) sortArray.push('-' + key); | ||
else sortArray.push(key); | ||
}); | ||
@@ -53,0 +53,0 @@ |
{ | ||
"name": "waterline", | ||
"description": "An ORM for Node.js and the Sails framework", | ||
"version": "0.10.0-rc5", | ||
"version": "0.10.0-rc6", | ||
"homepage": "http://github.com/balderdashy/waterline", | ||
@@ -26,3 +26,4 @@ "contributors": [ | ||
"waterline-schema": "~0.1.6", | ||
"node-switchback": "~0.0.4" | ||
"node-switchback": "~0.0.4", | ||
"waterline-criteria": "~0.10.3" | ||
}, | ||
@@ -29,0 +30,0 @@ "devDependencies": { |
275310
90
7354
7
+ Addedwaterline-criteria@~0.10.3
+ Addedinclude-all@0.1.6(transitive)
+ Addedunderscore.string@2.3.1(transitive)
+ Addedwaterline-criteria@0.10.7(transitive)