@themost/data
Version:
MOST Web Framework 2.0 - ORM module
246 lines (238 loc) • 10.3 kB
JavaScript
/**
* @license
* MOST Web Framework 2.0 Codename Blueshift
* Copyright (c) 2017, THEMOST LP All rights reserved
*
* Use of this source code is governed by an BSD-3-Clause license that can be
* found in the LICENSE file at https://themost.io/license
*/
///
var async = require('async');
var HasParentJunction = require('./has-parent-junction').HasParentJunction;
var DataObjectJunction = require('./data-object-junction').DataObjectJunction;
var DataError = require('@themost/common/errors').DataError;
var _ = require('lodash');
/**
* @module @themost/data/data-ref-object-listener
* @ignore
*/
/**
* @class
* @constructor
*/
function DataReferencedObjectListener() {
//
}
/**
* @private
* @param {DataEventArgs} event
* @param {DataAssociationMapping} mapping
* @param {Function} callback
*/
function beforeRemoveAssociatedObjects(event, mapping, callback) {
if (mapping.associationType !== 'association') {
return callback(new TypeError('Invalid association type. Expected a valid foreign key association.'));
}
if (mapping.parentModel !== event.model.name) {
return callback(new TypeError('Invalid association type. Expected a valid referenced key association.'));
}
var context = event.model.context;
var parentModel = event.model,
silent = event.model.$silent,
target = event.model.convert(event.target),
childModel = context.model(mapping.childModel),
parentField = event.model.getAttribute(mapping.parentField),
childField = childModel.getAttribute(mapping.childField);
parentModel.where(parentModel.primaryKey).equal(target[parentModel.primaryKey])
.select(parentField.name).silent().flatten().value()
.then(function(parentKey) {
if (_.isNil(parentKey)) {
return callback();
}
return childModel.where(mapping.childField).equal(parentKey).count().then(function(count) {
if (count>0) {
mapping.cascade = mapping.cascade || 'none';
if (mapping.cascade === 'none') {
return callback(new DataError('EFKEY','Cannot delete this object since it is being referenced by another entity.',null,childModel.name, childField.name));
}
else if (mapping.cascade === 'null' || mapping.cascade === 'default') {
return childModel.where(mapping.childField).equal(target[mapping.parentField])
.select(childModel.primaryKey, childModel.childField)
.silent()
.flatten()
.all().then(function(items) {
var childKey = childField.property || childField.name;
_.forEach(items, function(x) {
if (x.hasOwnProperty(childKey)) {
x[childKey] = null;
}
else {
x[childKey] = null;
}
});
return childModel.silent(silent).save(items).then(function() {
return callback();
});
});
}
else if (mapping.cascade === 'delete') {
return childModel.where(mapping.childField).equal(target[mapping.parentField])
.select(childModel.primaryKey)
.silent()
.flatten()
.all().then(function(items) {
return childModel.silent(silent).remove(items).then(function() {
return callback();
});
});
}
else {
return callback(new DataError('EATTR', 'Invalid cascade action', childModel.name, childField.name));
}
}
else {
return callback();
}
});
}).catch(function(err) {
return callback(err);
});
}
/**
* @private
* @param {DataEventArgs} event
* @param {DataAssociationMapping} mapping
* @param {Function} callback
*/
function beforeRemoveParentConnectedObjects(event, mapping, callback) {
if (mapping.associationType !== 'junction') {
return callback(new TypeError('Invalid association type. Expected a valid junction.'));
}
if (mapping.childModel !== event.model.name) {
return callback();
}
var childModel = event.model,
silent = event.model.$silent,
target = event.model.convert(event.target),
childField = childModel.getAttribute(mapping.childField);
var junction = new DataObjectJunction(target, mapping);
return childModel.where(childModel.primaryKey).equal(target.getId())
.select(childField.name).silent().flatten().value()
.then(function(childKey) {
if (_.isNil(childKey)) {
return callback();
}
var baseModel = junction.getBaseModel();
baseModel.where(junction.getValueField()).equal(childKey)
.select(baseModel.primaryKey)
.silent()
.all().then(function(items) {
mapping.cascade = mapping.cascade || 'none';
if (mapping.cascade === 'none') {
if (items.length === 0) {
return callback();
}
return callback(new DataError('EFKEY','Cannot delete this object since it is being referenced by another entity.',null,childModel.name, childField.name));
}
else if (mapping.cascade === 'delete' || mapping.cascade === 'null' || mapping.cascade === 'default') {
return baseModel.silent(silent).remove(items).then(function() {
return callback();
});
}
else {
return callback(new DataError('EATTR', 'Invalid cascade action', childModel.name, childField.name));
}
}).catch(function(err) {
return callback(err);
});
});
}
/**
* @private
* @param {DataEventArgs} event
* @param {DataAssociationMapping} mapping
* @param {Function} callback
*/
function beforeRemoveChildConnectedObjects(event, mapping, callback) {
var context = event.model.context;
if (mapping.associationType !== 'junction') {
return callback(new TypeError('Invalid association type. Expected a valid junction.'));
}
if (mapping.parentModel !== event.model.name) {
return callback();
}
var childModel = context.model(mapping.childModel),
silent = event.model.$silent,
target = event.model.convert(event.target),
parentModel = event.model,
parentField = parentModel.getAttribute(mapping.parentField);
var junction = new HasParentJunction(target, mapping);
return parentModel.where(parentModel.primaryKey).equal(target.getId())
.select(parentField.name).silent().flatten().value()
.then(function(parentKey) {
if (_.isNil(parentKey)) {
return callback();
}
var baseModel = junction.getBaseModel();
baseModel.where(junction.getObjectField()).equal(parentKey)
.select(baseModel.primaryKey)
.silent()
.all().then(function(items) {
mapping.cascade = mapping.cascade || 'none';
if (mapping.cascade === 'none') {
if (items.length===0) {
return callback();
}
return callback(new DataError('EFKEY','Cannot delete this object since it is being referenced by another entity.',null,parentModel.name, parentField.name));
}
else if (mapping.cascade === 'delete' || mapping.cascade === 'null' || mapping.cascade === 'default') {
if (items.length===0) {
return callback();
}
return baseModel.silent(silent).remove(items).then(function() {
return callback();
});
}
else {
return callback(new DataError('EATTR', 'Invalid cascade action', parentModel.name, parentField.name));
}
}).catch(function(err) {
return callback(err);
});
});
}
/**
* @param {DataEventArgs} event
* @param {function(Error=)} callback
*/
DataReferencedObjectListener.prototype.beforeRemove = function (event, callback) {
return event.model.getReferenceMappings(false).then(function(mappings) {
async.eachSeries(mappings,
/**
* @param {DataAssociationMapping} mapping
* @param {Function} cb
*/
function(mapping, cb) {
if (mapping.associationType === 'association') {
return beforeRemoveAssociatedObjects(event, mapping, cb);
}
else if (mapping.associationType === 'junction' && mapping.parentModel === event.model.name) {
return beforeRemoveChildConnectedObjects(event, mapping, cb);
}
else if (mapping.associationType === 'junction' && mapping.childModel === event.model.name) {
return beforeRemoveParentConnectedObjects(event, mapping, cb);
}
else {
return cb();
}
}, function(err) {
callback(err);
});
}).catch(function(err) {
return callback(err);
});
};
if (typeof exports !== 'undefined')
{
module.exports.DataReferencedObjectListener = DataReferencedObjectListener;
}