webgme-engine
Version:
WebGME server and Client API without a GUI
249 lines (214 loc) • 8.62 kB
JavaScript
/*globals define*/
/*eslint-env node, browser*/
/**
* @author pmeijer / https://github.com/pmeijer
*/
define(['common/core/users/metarules', 'q'], function (metaRules, Q) {
'use strict';
var CONSTRAINT_TYPES = {
META: 'META',
CUSTOM: 'CUSTOM',
BOTH: 'BOTH'
};
/**
* @param {object} core
* @param {object} mainLogger
* @constructor
*/
function ConstraintChecker(core, mainLogger) {
this.core = core;
this.logger = mainLogger.fork('ConstraintChecker');
this.customContraints = {};
this.customContraintsStorage = {};
// Set after initialize
this.initialized = false;
this.rootNode = null;
this.type = null;
this.commitHash = null;
}
ConstraintChecker.prototype.reinitialize =
ConstraintChecker.prototype.initialize = function (rootNode, commitHash, constraintType) {
this.rootNode = rootNode;
this.logger.debug('ConstraintChecker constraintType, commitHash', constraintType, commitHash);
this.type = constraintType || CONSTRAINT_TYPES.META;
this.commitHash = commitHash;
this.initialized = true;
};
ConstraintChecker.prototype._loadNode = function (path, callback) {
var self = this,
deferred = Q.defer();
if (this.initialized === false) {
deferred.reject(new Error('ConstraintChecker was never initialized!'));
} else {
self.core.loadByPath(self.rootNode, path, function (err, node) {
if (err) {
deferred.reject(new Error(err));
} else if (node === null) {
deferred.reject(new Error('Given nodePath does not exist "' + path + '"!'));
} else {
deferred.resolve(node);
}
});
}
return deferred.promise.nodeify(callback);
};
ConstraintChecker.prototype._checkNode = function (node, callback) {
var self = this,
deferred = Q.defer(),
//TODO: These messages need some sort of type/class!!
message = {
info: 'node [' + self.core.getPath(node) + '] validation',
_path: self.core.getPath(node),
_name: self.core.getAttribute(node, 'name') || 'N/A',
commit: self.commitHash,
hasViolation: false
},
promises = [],
customNames,
checkCustom = function (name) {
return self._executeCustomConstraint(node, name);
};
if (self.type === CONSTRAINT_TYPES.META) {
promises.push(metaRules.checkNode(self.core, node));
customNames = [];
} else if (self.type === CONSTRAINT_TYPES.CUSTOM) {
customNames = self.core.getConstraintNames(node);
promises = customNames.map(checkCustom);
} else if (self.type === CONSTRAINT_TYPES.BOTH) {
customNames = self.core.getConstraintNames(node);
promises = customNames.map(checkCustom);
promises.push(metaRules.checkNode(self.core, node));
} else {
deferred.reject(new Error('Unknown CONSTRAINT_TYPE: ' + self.type));
}
Q.allSettled(promises)
.then(function (results) {
var counter = 0;
results.map(function (result) {
var msg = {
hasViolation: false,
message: ''
};
counter += 1;
if (result.state === 'rejected') {
msg.message = result.reason instanceof Error ? result.reason.message : result.reason;
msg.hasViolation = true;
} else if (result.state === 'fulfilled') {
msg.message = result.value.message;
msg.hasViolation = result.value.hasViolation;
// Allow sending messages
msg.messages = result.value.messages;
} else {
msg.message = 'Unknown Q promise state ' + result.state;
msg.hasViolation = true;
}
if (counter > customNames.length) {
// This is the meta constraint.
message.META_RULES = msg;
} else {
message[customNames[counter - 1]] = msg;
}
if (msg.hasViolation) {
// Propagate the violation
message.hasViolation = true;
}
});
deferred.resolve(message);
})
.catch(deferred.reject);
return deferred.promise.nodeify(callback);
};
ConstraintChecker.prototype._executeCustomConstraint = function (node, name, callback) {
var self = this,
deferred = Q.defer(),
script = self.core.getConstraint(node, name).script;
if (!self.customContraints[script]) {
var a = '';
eval('a = ' + script + ';');
self.customContraints[script] = function (core, node, constraintCallback) {
try {
a(core, node, constraintCallback);
} catch (e) {
constraintCallback('Exception was thrown during "' + name + '" constraint execution:\n' +
e.toString());
}
};
self.customContraintsStorage[script] = {};
}
self.customContraints[script].call(self.customContraintsStorage[script], self.core, node,
function (err, result) {
if (err) {
deferred.reject(err instanceof Error ? err : new Error(err));
} else {
deferred.resolve(result);
}
});
return deferred.promise.nodeify(callback);
};
ConstraintChecker.prototype.checkModel = function (nodePath, callback) {
var self = this,
deferred = Q.defer(),
message = {
info: '',
commit: self.commitHash,
hasViolation: false
};
function checkChild(node, done) {
self._checkNode(node)
.then(function (res) {
if (res && res.hasViolation === true) {
message.hasViolation = true;
message[self.core.getGuid(node)] = res;
}
done();
})
.catch(done);
}
self._loadNode(nodePath)
.then(function (node) {
if (self.core.getPath(node) === self.core.getPath(self.core.getRoot(node))) {
message.info = 'Project Validation';
} else {
message.info = 'Model "' + (self.core.getAttribute(node, 'name') || '') +
'" [' + nodePath + '] Validation';
}
return self.core.traverse(node, null, checkChild);
})
.then(function () {
deferred.resolve(message);
})
.catch(function (err) {
deferred.reject(err);
});
return deferred.promise.nodeify(callback);
};
ConstraintChecker.prototype.checkNode = function (nodePath, callback) {
var self = this,
node,
deferred = Q.defer();
self._loadNode(nodePath)
.then(function (node_) {
node = node_;
return self._checkNode(node);
})
.then(function (result) {
var message = {
info: 'Node "' + (self.core.getAttribute(node, 'name') || '') +
'" [' + nodePath + '] Validation',
commit: self.commitHash,
hasViolation: false
};
message[self.core.getGuid(node)] = result;
message.hasViolation = result.hasViolation;
deferred.resolve(message);
})
.catch(function (err) {
deferred.reject(err);
});
return deferred.promise.nodeify(callback);
};
return {
Checker: ConstraintChecker,
TYPES: CONSTRAINT_TYPES
};
});