salix/loopback/common/models/loggable.js

228 lines
8.2 KiB
JavaScript

const pick = require('object.pick');
const LoopBackContext = require('loopback-context');
module.exports = function(Self) {
Self.setup = function() {
Self.super_.setup.call(this);
};
Self.observe('after save', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
await logInModel(ctx, loopBackContext);
});
Self.observe('before save', async function(ctx) {
let oldInstance;
let oldInstanceFk;
let newInstance;
if (ctx.data) {
oldInstanceFk = pick(ctx.currentInstance, Object.keys(ctx.data));
newInstance = await fkToValue(ctx.data, ctx);
oldInstance = await fkToValue(oldInstanceFk, ctx);
if (ctx.where && !ctx.currentInstance) {
let fields = Object.keys(ctx.data);
ctx.oldInstances = await Self.modelBuilder.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields});
}
}
if (ctx.isNewInstance) {
newInstance = await fkToValue(ctx.instance.__data, ctx);
}
ctx.hookState.oldInstance = oldInstance;
ctx.hookState.newInstance = newInstance;
});
Self.observe('before delete', async function(ctx) {
if (ctx.where) {
let affectedModel = ctx.Model.definition.name;
let definition = ctx.Model.definition;
let deletedInstances = await Self.modelBuilder.models[affectedModel].find({where: ctx.where});
let relation = definition.settings.log.relation;
if (relation) {
let primaryKey = ctx.Model.relations[relation].keyFrom;
let arrangedDeletedInstances = [];
for (let i = 0; i < deletedInstances.length; i++) {
if (primaryKey)
deletedInstances[i].originFk = deletedInstances[i][primaryKey];
let arrangedInstance = await fkToValue(deletedInstances[i], ctx);
arrangedDeletedInstances[i] = arrangedInstance;
}
ctx.hookState.oldInstance = arrangedDeletedInstances;
}
}
});
Self.observe('after delete', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
if (ctx.hookState.oldInstance)
logDeletedInstances(ctx, loopBackContext);
});
async function logDeletedInstances(ctx, loopBackContext) {
ctx.hookState.oldInstance.forEach(async instance => {
let userFk;
if (loopBackContext)
userFk = loopBackContext.active.accessToken.userId;
let definition = ctx.Model.definition;
let changedModelValue = definition.settings.log.changedModelValue;
let logRecord = {
originFk: instance.originFk,
userFk: userFk,
action: 'delete',
changedModel: ctx.Model.definition.name,
changedModelId: instance.id,
changedModelValue: instance[changedModelValue],
oldInstance: instance,
newInstance: {}
};
let transaction = {};
if (ctx.options && ctx.options.transaction) {
transaction = ctx.options.transaction;
}
let logModel = definition.settings.log.model;
await Self.modelBuilder.models[logModel].create(logRecord, transaction);
});
}
async function fkToValue(instance, ctx) {
let cleanInstance = JSON.parse(JSON.stringify(instance));
let result = {};
for (let key in cleanInstance) {
let val = cleanInstance[key];
if (val === undefined || val === null) continue;
for (let key1 in ctx.Model.relations) {
let val1 = ctx.Model.relations[key1];
if (val1.keyFrom == key && key != 'id') {
let recordSet = await val1.modelTo.findById(val);
val = recordSet.name; // FIXME preparar todos los modelos con campo name
break;
}
}
result[key] = val;
}
return result;
}
async function logInModel(ctx, loopBackContext) {
let definition = ctx.Model.definition;
let primaryKey;
for (let property in definition.properties) {
if (definition.properties[property].id) {
primaryKey = property;
break;
}
}
if (!primaryKey) throw new Error('Primary key not found');
let originId;
// RELATIONS LOG
let changedModelId;
if (ctx.instance && !definition.settings.log.relation) {
originId = ctx.instance.id;
changedModelId = ctx.instance.id;
} else if (definition.settings.log.relation) {
primaryKey = ctx.Model.relations[definition.settings.log.relation].keyFrom;
if (ctx.where && ctx.where[primaryKey])
originId = ctx.where[primaryKey];
else {
originId = ctx.instance[primaryKey];
changedModelId = ctx.instance.id;
}
} else {
originId = ctx.currentInstance.id;
changedModelId = ctx.currentInstance.id;
}
// Sets the changedModelValue to save and the instances changed in case its an updateAll
let changedModelValue = definition.settings.log.changedModelValue;
if (changedModelValue && (!ctx.instance || !ctx.instance[changedModelValue])) {
var where = [];
changedModelId = [];
let changedInstances = await Self.modelBuilder.models[definition.name].find({where: ctx.where, fields: ['id', changedModelValue]});
changedInstances.forEach(element => {
where.push(element[changedModelValue]);
changedModelId.push(element.id);
});
} else if (ctx.hookState.oldInstance) {
where = ctx.instance[changedModelValue];
}
// Set oldInstance, newInstance, userFk and action
let oldInstance = {};
if (ctx.hookState.oldInstance) {
Object.assign(oldInstance, ctx.hookState.oldInstance);
}
let newInstance = {};
if (ctx.hookState.newInstance) {
Object.assign(newInstance, ctx.hookState.newInstance);
}
let userFk;
if (loopBackContext)
userFk = loopBackContext.active.accessToken.userId;
let action = setActionType(ctx);
let logRecord = {
originFk: originId,
userFk: userFk,
action: action,
changedModel: ctx.Model.definition.name,
changedModelId: changedModelId,
changedModelValue: where,
oldInstance: oldInstance,
newInstance: newInstance
};
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
let logModel = definition.settings.log.model;
let transaction = {};
if (ctx.options && ctx.options.transaction) {
transaction = ctx.options.transaction;
}
await Self.modelBuilder.models[logModel].create(logsToSave, transaction);
}
// this function retuns all the instances changed in case this is an updateAll
function setLogsToSave(changedInstances, changedInstancesIds, logRecord, ctx) {
let promises = [];
if (changedInstances && typeof changedInstances == "object") {
for (let i = 0; i < changedInstances.length; i++) {
logRecord.changedModelId = changedInstancesIds[i];
logRecord.changedModelValue = changedInstances[i];
if (ctx.oldInstances)
logRecord.oldInstance = ctx.oldInstances[i];
promises.push(JSON.parse(JSON.stringify(logRecord)));
}
} else {
return logRecord;
}
return promises;
}
function setActionType(ctx) {
let oldInstance = ctx.hookState.oldInstance;
let newInstance = ctx.hookState.newInstance;
if (oldInstance && newInstance) {
return 'update';
} else if (!oldInstance && newInstance) {
return 'insert';
}
return 'delete';
}
};