New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loopback relations validate #114
Comments
Any link on how to validate it manually if this cannot be done automatically? I found this http://stackoverflow.com/questions/26785576/loopback-validation-on-properties-whos-types-are-other-models-version-2-xx but I'm looking for something that only validates the presence of the other model. |
I've found a simpler way: strongloop/loopback#505, it only checks the presence of an id but not that the object actually exists. |
@wesleymilan and @aalexgabi let's say we have User model and Role model and in a mixins file we could have this: 'use strict'
let _ = require('lodash');
let checkIntegrityThroughModel = function(ctx, next) {
if (ctx.instance) {
let relations = ctx.Model.definition.settings.relations;
// get models from relations and findById w/ val from foreignKey
// that matches ctx.instance key/val
let throughInfo = _.map(relations, relation => {
return { modelName: relation.model, fk: relation.foreignKey };
});
// ES6 destructuring
var [
{ modelName: modelNameA, fk: fkA },
{ modelName: modelNameB, fk: fkB }
] = throughInfo;
let throughModel = ctx.Model;
let modelA = throughModel.app.models[modelNameA];
let modelB = throughModel.app.models[modelNameB];
let valueAId = ctx.instance[fkA];
let valueBId = ctx.instance[fkB];
modelA.findById(valueAId).then(function(instanceA) {
if (instanceA === null) {
return next('No ' + modelNameA + ' with "' + valueAId + '" id');
}
modelB.findById(valueBId).then(function(instanceB) {
if (instanceB === null) {
return next('No ' + modelNameB + ' with "' + valueBId + '" id');
}
next();
});
}).catch(function(err) {
next(err);
});
}
};
module.exports = function(Model, options) {
/**
* @api {POST} /ThroughModel/ Create ModelA-ModelB information
* @apiDescription Before Create a row in the through model, we check
* both ids exist => integrity
* This is both fk must be existing rows as pk in their tables
*
* @apiName beforeCreate
* @apiGroup Model
*
* @apiParam {String} modelAId ModelA unique ID.
* @apiParam {String} modelBId ModelB unique ID.
*
* @apiParamExample {json} Request-Example (/User-Role):
* {
* "userId": "490dd640-862e-11e6-9644-b5c34ca53299",
* "roleId": "basic",
* }
*/
Model.observe('before save', checkIntegrityThroughModel);
}; This example is for a through model with 2 keys, but this is easily extensible to and in the model definition Model.json (we should include this in every model we want the validation) "mixins": {
"NameOfMixinsFile": { /* options */ }
} I know it's not the best as we expect to be validated by the connector but in the meantime. |
Elaborating on jesusbv proposal : that is possible by creating and using mixins (sorry for the comments in French) /common/mixins/BelongsToIntegrityCheck.js /*Ce mixin permet de vérifier les contraintes d'intégrité (l'objet enfant possède bien un parent)
en vérifiant que les clés étrangères des objets parents existes bien lors de la création d'un objet enfant*/
'use strict'
let _ = require('lodash');
let checkBelongsToIntegrity = function (ctx, next) {
if (ctx.instance) {
let relations = ctx.Model.definition.settings.relations;
let relationsArray = _.map(relations, rel => {
return { modelName: rel.model, fk: rel.foreignKey, type: rel.type };
});
/* On utilise Lodash pour transformer l'objet des relations en Tableau de la forme
[
{ modelName: 'achat', fk: 'achat_id', type: 'belongsTo' },
{ modelName: 'FED_AGENT', fk: 'agent_rfagent', type: 'belongsTo' }
]
*/
let thisModel = ctx.Model;
// Le message qui sera renvoyé en cas d'échec de vérification des contraintes d'intégrité
let message = "";
// Le tableau des promises correspondant aux requêtes vérifiants les contraintes
let promiseArray = [];
relationsArray.forEach(function (relation) {
if (relation.type == 'belongsTo') {
let parentModelName = relation.modelName;
let parentModel = thisModel.app.models[parentModelName];
let parentId = ctx.instance[relation.fk];
// On cherche le modèle parent qui correspond à l'id demandé pour le modèle enfant...
promiseArray.push(parentModel.findById(parentId).then(function (parentInstance) {
if (parentInstance === null) {
message += 'No ' + parentModelName + ' with "' + parentId + '" id. ';
}
}));
}
}
);
/* Une fois que toutes les promesses ont été déterminées et conduisent vers un message en cas de non respect de la contrainte d'intégrité,
on les regroupe dans une promesse commune résolue quand toutes sont résolues et qui renvoit le message en cas de non respect de contrainte */
Promise.all(promiseArray)
.then(
function () {
next(message);
}
, console.error)
.catch(function (err) {
next(err);
});
}
}
module.exports = function (Model, options) {
Model.observe('before save', checkBelongsToIntegrity);
}; /common/mixins/HasManyIntegrityCheck.js /*Ce mixin permet de vérifier les contraintes d'intégrité (si l'objet parent possède des enfants, on ne peut pas le supprimer)
en faisant une requête sur les enfants qui sont rattachés à ce parent*/
'use strict'
let _ = require('lodash');
let checkHasManyIntegrity = function (ctx, next) {
if (ctx.where) {
let relations = ctx.Model.definition.settings.relations;
let relationsArray = _.map(relations, rel => {
return { modelName: rel.model, fk: rel.foreignKey, type: rel.type };
});
/* On utilise Lodash pour transformer l'objet des relations en Tableau de la forme
[
{ modelName: 'achat', fk: 'achat_id', type: 'belongsTo' },
{ modelName: 'FED_AGENT', fk: 'agent_rfagent', type: 'belongsTo' }
]
*/
let thisModel = ctx.Model;
// Le message qui sera renvoyé en cas d'échec de vérification des contraintes d'intégrité
let message = "";
// Le tableau des promises correspondant aux requêtes vérifiants les contraintes
let promiseArray = [];
relationsArray.forEach(function (relation) {
if (relation.type == 'hasMany') {
let childrenModelName = relation.modelName;
let childrenModel = thisModel.app.models[childrenModelName];
let parentId = ctx.where.id;
let whereObject = {};
whereObject[relation.fk] = ctx.where.id;
// On cherche les enfants éventuels
promiseArray.push(childrenModel.find({
where: whereObject
}).then(function (data) {
if (data.length > 0) { // Si il y a des enfants, on renvoit une erreur
message += 'This "' + thisModel.modelName + '" has "' + childrenModelName + '", and can\'t be deleted. ';
}
}));
}
}
);
/* Une fois que toutes les promesses ont été déterminées et conduisent vers un message en cas de non respect de la contrainte d'intégrité,
on les regroupe dans une promesse commune résolue quand toutes sont résolues et qui renvoit le message en cas de non respect de contrainte */
Promise.all(promiseArray)
.then(
function () {
next(message);
}
, console.error)
.catch(function (err) {
next(err);
});
}
}
module.exports = function (Model, options) {
Model.observe('before delete', checkHasManyIntegrity);
}; And in your models : "mixins": {
"HasManyIntegrityCheck": {}
}, Children Model : "mixins": {
"BelongsToIntegrityCheck": {}
}, |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
I think this should be kept alive. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
This issue has been closed due to continued inactivity. Thank you for your understanding. If you believe this to be in error, please contact one of the code owners, listed in the |
Loopback does not validate relations by default, it should have some parameter on model.json to activate a default forward key validation, just in case of use NoSQL or multiples databases.
In my case I have a MySQL database builded to my legacy application, and I need to migrate gradually to my public API app, but even when it's done, I wan't to keep some integrity between payment informations in my SQL database, and high traffic data in my NoSQL database, so this solution could keep my code cleaned and fast to implement.
Thank you
The text was updated successfully, but these errors were encountered: