Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
language: node_js
node_js:
- "0.12"
- "0.10"
- "iojs"
- "0.12"
- "4"
- "5"
after_script:
- npm run coverage && cat ./coverage/lcov.info | ./node_modules/.bin/codeclimate
addons:
code_climate:
repo_token: 351483555263cf9bcd2416c58b0e0ae6ca1b32438aa51bbab2c833560fb67cc0
sudo: false
110 changes: 65 additions & 45 deletions lib/waterline/model/lib/associationMethods/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ var Add = module.exports = function(collection, proto, records, cb) {

this.primaryKey = this.findPrimaryKey(attributes, values);

if(!this.primaryKey) return cb(new Error('No Primary Key set to associate the record with! ' +
if (!this.primaryKey) {
return cb(new Error('No Primary Key set to associate the record with! ' +
'Try setting an attribute as a primary key or include an ID property.'));
}

if(!proto.toObject()[this.primaryKey]) return cb(new Error('No Primary Key set to associate the record with! ' +
if (!proto.toObject()[this.primaryKey]) {
return cb(new Error('No Primary Key set to associate the record with! ' +
'Primary Key must have a value, it can\'t be an optional value.'));
}

// Loop through each of the associations on this model and add any associations
// that have been specified. Do this in series and limit the actual saves to 10
Expand All @@ -60,15 +64,15 @@ var Add = module.exports = function(collection, proto, records, cb) {
Add.prototype.findPrimaryKey = function(attributes, values) {
var primaryKey = null;

for(var attribute in attributes) {
if(hasOwnProperty(attributes[attribute], 'primaryKey') && attributes[attribute].primaryKey) {
for (var attribute in attributes) {
if (hasOwnProperty(attributes[attribute], 'primaryKey') && attributes[attribute].primaryKey) {
primaryKey = attribute;
break;
}
}

// If no primary key check for an ID property
if(!primaryKey && hasOwnProperty(values, 'id')) primaryKey = 'id';
if (!primaryKey && hasOwnProperty(values, 'id')) primaryKey = 'id';

return primaryKey;
};
Expand All @@ -89,7 +93,7 @@ Add.prototype.createCollectionAssociations = function(records, cb) {
},

function(err) {
if(err || self.failedTransactions.length > 0) {
if (err || self.failedTransactions.length > 0) {
return cb(null, self.failedTransactions);
}

Expand All @@ -114,36 +118,36 @@ Add.prototype.createAssociations = function(key, records, cb) {
var attribute = this.collection._attributes[key];
var collectionName = attribute.collection.toLowerCase();
var associatedCollection = this.collection.waterline.collections[collectionName];
var relatedPK = _.find(associatedCollection.attributes, { primaryKey: true });
var relatedPK = _.find(associatedCollection.attributes, { primaryKey: true });
var schema = this.collection.waterline.schema[this.collection.identity].attributes[key];

// Limit Adds to 10 at a time to prevent the connection pool from being exhausted
async.eachLimit(records, 10, function(association, next) {

// If an object was passed in it should be created.
// This allows new records to be created through the association interface
if(association !== null && typeof association === 'object' && Object.keys(association).length > 0) {
if (association !== null && typeof association === 'object' && Object.keys(association).length > 0) {

// If a custom PK was used on the associated collection and it's not
// autoIncrementing, create the record. This allows nested
// creates to work when custom PK's are used.
if(!relatedPK || !relatedPK.autoIncrement && !associatedCollection.autoPK) {
return self.createNewRecord(associatedCollection, schema, association, next);
if (!relatedPK || !relatedPK.autoIncrement && !associatedCollection.autoPK) {
return self.createNewRecord(associatedCollection, schema, association, key, next);
}

// Check if the record contains a primary key, if so just link the values
if(hasOwnProperty(association, associatedCollection.primaryKey)) {
if (hasOwnProperty(association, associatedCollection.primaryKey)) {
var pk = associatedCollection.primaryKey;
return self.updateRecord(associatedCollection, schema, association[pk], next);
return self.updateRecord(associatedCollection, schema, association[pk], key, next);
}

return self.createNewRecord(associatedCollection, schema, association, next);
return self.createNewRecord(associatedCollection, schema, association, key, next);
}

// If the value is a primary key just update the association's foreign key
// This will either create the new association through a foreign key or re-associatiate
// with another collection.
self.updateRecord(associatedCollection, schema, association, next);
self.updateRecord(associatedCollection, schema, association, key, next);

}, cb);
};
Expand All @@ -158,24 +162,24 @@ Add.prototype.createAssociations = function(key, records, cb) {
* @api private
*/

Add.prototype.createNewRecord = function(collection, attribute, values, cb) {
Add.prototype.createNewRecord = function(collection, attribute, values, key, cb) {
var self = this;

// Check if this is a many-to-many by looking at the junctionTable flag
var schema = this.collection.waterline.schema[attribute.collection.toLowerCase()];
var junctionTable = schema.junctionTable || false;
var junctionTable = schema.junctionTable || schema.throughTable;

// If this isn't a many-to-many then add the foreign key in to the values
if(!junctionTable) {
if (!junctionTable) {
values[attribute.onKey] = this.proto[this.primaryKey];
}

collection.create(values, function(err, record) {
if(err) {
if (err) {

// If no via was specified and the insert failed on a one-to-many build up an error message that
// properly reflects the error.
if(!junctionTable && !hasOwnProperty(attribute, 'via')) {
if (!junctionTable && !hasOwnProperty(attribute, 'via')) {
err = new Error('You attempted to create a has many relationship but didn\'t link the two ' +
'atttributes together. Please setup a link using the via keyword.');
}
Expand All @@ -189,15 +193,15 @@ Add.prototype.createNewRecord = function(collection, attribute, values, cb) {
}

// if no junction table then return
if(!junctionTable) return cb();
if (!junctionTable) return cb();

// if junction table but there was an error don't try and link the records
if(err) return cb();
if (err) return cb();

// Find the collection's Primary Key value
var primaryKey = self.findPrimaryKey(collection._attributes, record.toObject());

if(!primaryKey) {
if (!primaryKey) {
self.failedTransactions.push({
type: 'insert',
collection: collection.identity,
Expand All @@ -211,7 +215,7 @@ Add.prototype.createNewRecord = function(collection, attribute, values, cb) {

// The related record was created now the record in the junction table
// needs to be created to link the two records
self.createManyToMany(joinCollection, attribute, record[primaryKey], cb);
self.createManyToMany(joinCollection, attribute, record[primaryKey], key, cb);
});
};

Expand All @@ -225,37 +229,39 @@ Add.prototype.createNewRecord = function(collection, attribute, values, cb) {
* @api private
*/

Add.prototype.updateRecord = function(collection, attribute, values, cb) {
Add.prototype.updateRecord = function(collection, attribute, pk, key, cb) {
var self = this;

// Check if this is a many-to-many by looking at the junctionTable flag
var schema = this.collection.waterline.schema[attribute.collection.toLowerCase()];
var junctionTable = schema.junctionTable || false;
var junctionTable = schema.junctionTable || schema.throughTable;

// If so build out the criteria and create a new record in the junction table
if(junctionTable) {
if (junctionTable) {
var joinCollection = this.collection.waterline.collections[attribute.collection.toLowerCase()];
return this.createManyToMany(joinCollection, attribute, values, cb);
return this.createManyToMany(joinCollection, attribute, pk, key, cb);
}

// Grab the associated collection's primaryKey
var attributes = this.collection.waterline.schema[collection.identity].attributes;
var associationKey = this.findPrimaryKey(attributes, attributes);

if(!associationKey) return cb(new Error('No Primary Key defined on the child record you ' +
'are trying to associate the record with! Try setting an attribute as a primary key or ' +
'include an ID property.'));
if (!associationKey) {
return cb(new Error('No Primary Key defined on the child record you ' +
'are trying to associate the record with! Try setting an attribute as a primary key or ' +
'include an ID property.'));
}

// Build up criteria and updated values used to update the record
var criteria = {};
var _values = {};

criteria[associationKey] = values;
criteria[associationKey] = pk;
_values[attribute.onKey] = this.proto[this.primaryKey];

collection.update(criteria, _values, function(err) {

if(err) {
if (err) {
self.failedTransactions.push({
type: 'update',
collection: collection.identity,
Expand All @@ -279,16 +285,28 @@ Add.prototype.updateRecord = function(collection, attribute, values, cb) {
* @api private
*/

Add.prototype.createManyToMany = function(collection, attribute, pk, cb) {
Add.prototype.createManyToMany = function(collection, attribute, pk, key, cb) {
var self = this;

// Grab the associated collection's primaryKey
var collectionAttributes = this.collection.waterline.schema[attribute.collection.toLowerCase()];
var associationKey = collectionAttributes.attributes[attribute.on].via;

if(!associationKey) return cb(new Error('No Primary Key set on the child record you ' +
'are trying to associate the record with! Try setting an attribute as a primary key or ' +
'include an ID property.'));
// If this is a throughTable, look into the meta data cache for what key to use
if (collectionAttributes.throughTable) {
var cacheKey = collectionAttributes.throughTable[attribute.on + '.' + key];
if (!cacheKey) {
return cb(new Error('Unable to find the proper cache key in the through table definition'));
}

associationKey = cacheKey;
}

if (!associationKey) {
return cb(new Error('No Primary Key set on the child record you ' +
'are trying to associate the record with! Try setting an attribute as a primary key or ' +
'include an ID property.'));
}

// Build up criteria and updated values used to create the record
var criteria = {};
Expand All @@ -308,9 +326,11 @@ Add.prototype.createManyToMany = function(collection, attribute, pk, cb) {
_criteria[primaryKey] = pk;

associatedCollection.findOne(_criteria, function(err, record) {
if(err) return next(err);
if(!record) return next(new Error("Associated Record For " + associatedCollectionName +
" with " + primaryKey + " = " + pk + " No Longer Exists"));
if (err) return next(err);
if (!record) {
return next(new Error('Associated Record For ' + associatedCollectionName +
' with ' + primaryKey + ' = ' + pk + ' No Longer Exists'));
}

next();
});
Expand All @@ -320,8 +340,8 @@ Add.prototype.createManyToMany = function(collection, attribute, pk, cb) {

// First look up the record to ensure it doesn't exist
collection.findOne(criteria, function(err, val) {
if(err || val) {
return next(new Error("Trying to '.add()' an instance which already exists!"));
if (err || val) {
return next(new Error('Trying to \'.add()\' an instance which already exists!'));
}
next();
});
Expand All @@ -332,7 +352,7 @@ Add.prototype.createManyToMany = function(collection, attribute, pk, cb) {
}]

}, function(err) {
if(err) {
if (err) {
self.failedTransactions.push({
type: 'insert',
collection: collection.identity,
Expand All @@ -358,14 +378,14 @@ Add.prototype.createManyToMany = function(collection, attribute, pk, cb) {
Add.prototype.findAssociationKey = function(collection) {
var associationKey = null;

for(var attribute in collection.attributes) {
for (var attribute in collection.attributes) {
var attr = collection.attributes[attribute];
var identity = this.collection.identity;

if(!hasOwnProperty(attr, 'references')) continue;
if (!hasOwnProperty(attr, 'references')) continue;
var attrCollection = attr.references;

if(attrCollection !== identity) {
if (attrCollection !== identity) {
associationKey = attr.columnName;
}
}
Expand Down
Loading