2018-10-03 07:37:34 +00:00
|
|
|
const LoopBackContext = require('loopback-context');
|
2021-03-19 18:20:19 +00:00
|
|
|
const log = require('vn-loopback/util/log');
|
2018-10-03 07:37:34 +00:00
|
|
|
|
|
|
|
module.exports = function(Self) {
|
|
|
|
Self.setup = function() {
|
|
|
|
Self.super_.setup.call(this);
|
|
|
|
};
|
|
|
|
|
2018-10-22 12:35:21 +00:00
|
|
|
Self.observe('after save', async function(ctx) {
|
2018-10-03 07:37:34 +00:00
|
|
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
|
|
await logInModel(ctx, loopBackContext);
|
|
|
|
});
|
|
|
|
|
|
|
|
Self.observe('before save', async function(ctx) {
|
2020-05-26 12:37:34 +00:00
|
|
|
const appModels = ctx.Model.app.models;
|
2020-05-26 13:49:20 +00:00
|
|
|
const definition = ctx.Model.definition;
|
2021-03-19 18:20:19 +00:00
|
|
|
const modelName = definition.name;
|
|
|
|
const model = appModels[modelName];
|
2020-05-26 12:37:34 +00:00
|
|
|
const options = {};
|
|
|
|
|
|
|
|
// Check for transactions
|
2019-02-27 09:56:31 +00:00
|
|
|
if (ctx.options && ctx.options.transaction)
|
|
|
|
options.transaction = ctx.options.transaction;
|
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
let oldInstance;
|
|
|
|
let newInstance;
|
2018-10-22 12:35:21 +00:00
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
if (ctx.data) {
|
2021-03-19 18:20:19 +00:00
|
|
|
const changes = log.getChanges(ctx.currentInstance, ctx.data);
|
|
|
|
oldInstance = await log.translateValues(model, changes.old, options);
|
|
|
|
newInstance = await log.translateValues(model, changes.new, options);
|
2020-05-26 12:37:34 +00:00
|
|
|
|
2018-10-22 12:35:21 +00:00
|
|
|
if (ctx.where && !ctx.currentInstance) {
|
2020-05-26 12:37:34 +00:00
|
|
|
const fields = Object.keys(ctx.data);
|
|
|
|
|
|
|
|
ctx.oldInstances = await appModels[modelName].find({
|
|
|
|
where: ctx.where,
|
|
|
|
fields: fields
|
|
|
|
}, options);
|
2018-10-22 12:35:21 +00:00
|
|
|
}
|
2018-10-03 07:37:34 +00:00
|
|
|
}
|
2020-05-26 12:37:34 +00:00
|
|
|
|
|
|
|
// Get changes from created instance
|
2019-02-20 15:30:46 +00:00
|
|
|
if (ctx.isNewInstance)
|
2021-03-19 18:20:19 +00:00
|
|
|
newInstance = await log.translateValues(model, ctx.instance.__data, options);
|
2019-02-20 15:30:46 +00:00
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
ctx.hookState.oldInstance = oldInstance;
|
|
|
|
ctx.hookState.newInstance = newInstance;
|
|
|
|
});
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
Self.observe('before delete', async function(ctx) {
|
2021-03-19 18:20:19 +00:00
|
|
|
const models = ctx.Model.app.models;
|
2020-05-26 12:37:34 +00:00
|
|
|
const definition = ctx.Model.definition;
|
|
|
|
const relations = ctx.Model.relations;
|
|
|
|
|
2021-03-19 18:20:19 +00:00
|
|
|
const options = {};
|
2019-02-27 09:56:31 +00:00
|
|
|
if (ctx.options && ctx.options.transaction)
|
|
|
|
options.transaction = ctx.options.transaction;
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
if (ctx.where) {
|
2021-03-19 18:20:19 +00:00
|
|
|
const modelName = definition.name;
|
|
|
|
const model = models[modelName];
|
|
|
|
const deletedRows = await model.find({
|
2020-05-26 12:37:34 +00:00
|
|
|
where: ctx.where
|
|
|
|
}, options);
|
|
|
|
|
2021-03-19 18:20:19 +00:00
|
|
|
const relation = definition.settings.log.relation;
|
2018-10-18 09:11:17 +00:00
|
|
|
|
2018-10-22 12:35:21 +00:00
|
|
|
if (relation) {
|
2021-03-19 18:20:19 +00:00
|
|
|
const primaryKey = relations[relation].keyFrom;
|
2018-10-18 09:11:17 +00:00
|
|
|
|
2021-03-19 18:20:19 +00:00
|
|
|
const instances = [];
|
|
|
|
for (let instance of deletedRows) {
|
|
|
|
const translatedValues = await log.translateValues(model, instance, options);
|
2018-10-22 12:35:21 +00:00
|
|
|
if (primaryKey)
|
2021-03-19 18:20:19 +00:00
|
|
|
translatedValues.originFk = instance[primaryKey];
|
|
|
|
instances.push(translatedValues);
|
2018-10-22 12:35:21 +00:00
|
|
|
}
|
2021-03-19 18:20:19 +00:00
|
|
|
|
|
|
|
ctx.hookState.oldInstance = instances;
|
2018-10-22 12:35:21 +00:00
|
|
|
}
|
2018-10-03 07:37:34 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
Self.observe('after delete', async function(ctx) {
|
|
|
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
2018-10-22 12:35:21 +00:00
|
|
|
if (ctx.hookState.oldInstance)
|
|
|
|
logDeletedInstances(ctx, loopBackContext);
|
|
|
|
});
|
|
|
|
|
|
|
|
async function logDeletedInstances(ctx, loopBackContext) {
|
2020-05-26 12:37:34 +00:00
|
|
|
const appModels = ctx.Model.app.models;
|
2020-05-26 13:49:20 +00:00
|
|
|
const definition = ctx.Model.definition;
|
2019-02-27 09:56:31 +00:00
|
|
|
let options = {};
|
|
|
|
if (ctx.options && ctx.options.transaction)
|
|
|
|
options.transaction = ctx.options.transaction;
|
|
|
|
|
2018-10-22 12:35:21 +00:00
|
|
|
ctx.hookState.oldInstance.forEach(async instance => {
|
|
|
|
let userFk;
|
|
|
|
if (loopBackContext)
|
|
|
|
userFk = loopBackContext.active.accessToken.userId;
|
|
|
|
|
|
|
|
let changedModelValue = definition.settings.log.changedModelValue;
|
|
|
|
let logRecord = {
|
|
|
|
originFk: instance.originFk,
|
|
|
|
userFk: userFk,
|
|
|
|
action: 'delete',
|
2020-05-26 13:49:20 +00:00
|
|
|
changedModel: definition.name,
|
2018-10-22 12:35:21 +00:00
|
|
|
changedModelId: instance.id,
|
|
|
|
changedModelValue: instance[changedModelValue],
|
|
|
|
oldInstance: instance,
|
|
|
|
newInstance: {}
|
|
|
|
};
|
|
|
|
|
2019-04-04 10:38:54 +00:00
|
|
|
delete instance.originFk;
|
|
|
|
|
2018-10-22 12:35:21 +00:00
|
|
|
let logModel = definition.settings.log.model;
|
2020-05-26 12:37:34 +00:00
|
|
|
await appModels[logModel].create(logRecord, options);
|
2018-10-22 12:35:21 +00:00
|
|
|
});
|
|
|
|
}
|
2018-10-03 07:37:34 +00:00
|
|
|
|
|
|
|
async function logInModel(ctx, loopBackContext) {
|
2020-05-26 12:37:34 +00:00
|
|
|
const appModels = ctx.Model.app.models;
|
|
|
|
const definition = ctx.Model.definition;
|
|
|
|
const defSettings = ctx.Model.definition.settings;
|
|
|
|
const relations = ctx.Model.relations;
|
|
|
|
|
|
|
|
const options = {};
|
2019-02-27 09:56:31 +00:00
|
|
|
if (ctx.options && ctx.options.transaction)
|
|
|
|
options.transaction = ctx.options.transaction;
|
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
let primaryKey;
|
|
|
|
for (let property in definition.properties) {
|
|
|
|
if (definition.properties[property].id) {
|
|
|
|
primaryKey = property;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-10-18 09:11:17 +00:00
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
if (!primaryKey) throw new Error('Primary key not found');
|
|
|
|
let originId;
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
// RELATIONS LOG
|
|
|
|
let changedModelId;
|
|
|
|
|
2020-05-26 12:37:34 +00:00
|
|
|
if (ctx.instance && !defSettings.log.relation) {
|
2018-10-18 09:11:17 +00:00
|
|
|
originId = ctx.instance.id;
|
|
|
|
changedModelId = ctx.instance.id;
|
2020-05-26 12:37:34 +00:00
|
|
|
} else if (defSettings.log.relation) {
|
|
|
|
primaryKey = relations[defSettings.log.relation].keyFrom;
|
2018-10-03 07:37:34 +00:00
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
if (ctx.where && ctx.where[primaryKey])
|
|
|
|
originId = ctx.where[primaryKey];
|
2019-04-04 10:38:54 +00:00
|
|
|
else if (ctx.instance) {
|
2018-10-03 07:37:34 +00:00
|
|
|
originId = ctx.instance[primaryKey];
|
2018-10-18 09:11:17 +00:00
|
|
|
changedModelId = ctx.instance.id;
|
2018-10-03 07:37:34 +00:00
|
|
|
}
|
2018-10-18 09:11:17 +00:00
|
|
|
} else {
|
|
|
|
originId = ctx.currentInstance.id;
|
|
|
|
changedModelId = ctx.currentInstance.id;
|
2018-10-03 07:37:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
// Sets the changedModelValue to save and the instances changed in case its an updateAll
|
2020-05-26 12:37:34 +00:00
|
|
|
let showField = defSettings.log.showField;
|
2019-02-27 09:56:31 +00:00
|
|
|
let where;
|
2019-04-04 10:38:54 +00:00
|
|
|
if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) {
|
2018-10-18 09:11:17 +00:00
|
|
|
changedModelId = [];
|
2019-02-27 09:56:31 +00:00
|
|
|
where = [];
|
2020-05-26 12:37:34 +00:00
|
|
|
let changedInstances = await appModels[definition.name].find({
|
|
|
|
where: ctx.where,
|
|
|
|
fields: ['id', showField, primaryKey]
|
|
|
|
}, options);
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
changedInstances.forEach(element => {
|
2019-04-04 10:38:54 +00:00
|
|
|
where.push(element[showField]);
|
2018-10-18 09:11:17 +00:00
|
|
|
changedModelId.push(element.id);
|
2019-04-04 10:38:54 +00:00
|
|
|
originId = element[primaryKey];
|
2018-10-18 09:11:17 +00:00
|
|
|
});
|
2019-02-20 15:30:46 +00:00
|
|
|
} else if (ctx.hookState.oldInstance)
|
2019-04-04 10:38:54 +00:00
|
|
|
where = ctx.instance[showField];
|
2019-02-20 15:30:46 +00:00
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
// Set oldInstance, newInstance, userFk and action
|
2018-10-03 07:37:34 +00:00
|
|
|
let oldInstance = {};
|
2019-02-20 15:30:46 +00:00
|
|
|
if (ctx.hookState.oldInstance)
|
2018-10-03 07:37:34 +00:00
|
|
|
Object.assign(oldInstance, ctx.hookState.oldInstance);
|
|
|
|
|
|
|
|
let newInstance = {};
|
2019-02-20 15:30:46 +00:00
|
|
|
if (ctx.hookState.newInstance)
|
2018-10-18 09:11:17 +00:00
|
|
|
Object.assign(newInstance, ctx.hookState.newInstance);
|
2018-10-03 10:54:13 +00:00
|
|
|
let userFk;
|
2018-10-18 09:11:17 +00:00
|
|
|
if (loopBackContext)
|
|
|
|
userFk = loopBackContext.active.accessToken.userId;
|
2018-10-03 10:54:13 +00:00
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
let action = setActionType(ctx);
|
2018-10-03 10:54:13 +00:00
|
|
|
|
2020-05-26 12:37:34 +00:00
|
|
|
removeUnloggable(definition, oldInstance);
|
|
|
|
removeUnloggable(definition, newInstance);
|
2019-09-12 07:49:02 +00:00
|
|
|
|
2020-06-04 12:44:38 +00:00
|
|
|
// Prevent log with no new changes
|
2020-06-04 12:46:55 +00:00
|
|
|
const hasNewChanges = Object.keys(newInstance).length;
|
2020-06-04 12:44:38 +00:00
|
|
|
if (!hasNewChanges) return;
|
|
|
|
|
2018-10-03 07:37:34 +00:00
|
|
|
let logRecord = {
|
|
|
|
originFk: originId,
|
|
|
|
userFk: userFk,
|
|
|
|
action: action,
|
2020-05-26 12:37:34 +00:00
|
|
|
changedModel: definition.name,
|
2020-01-21 08:00:42 +00:00
|
|
|
changedModelId: changedModelId, // Model property with an different data type will throw a NaN error
|
2018-10-18 09:11:17 +00:00
|
|
|
changedModelValue: where,
|
2018-10-03 07:37:34 +00:00
|
|
|
oldInstance: oldInstance,
|
|
|
|
newInstance: newInstance
|
|
|
|
};
|
|
|
|
|
2018-10-22 12:35:21 +00:00
|
|
|
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
|
2020-05-26 12:37:34 +00:00
|
|
|
let logModel = defSettings.log.model;
|
2018-10-03 07:37:34 +00:00
|
|
|
|
2020-05-26 12:37:34 +00:00
|
|
|
await appModels[logModel].create(logsToSave, options);
|
2018-10-18 09:11:17 +00:00
|
|
|
}
|
|
|
|
|
2019-09-12 07:49:02 +00:00
|
|
|
/**
|
|
|
|
* Removes unwanted properties
|
|
|
|
* @param {*} definition Model definition
|
|
|
|
* @param {*} properties Modified object properties
|
|
|
|
*/
|
2020-05-26 12:37:34 +00:00
|
|
|
function removeUnloggable(definition, properties) {
|
2019-09-12 07:49:02 +00:00
|
|
|
const propList = Object.keys(properties);
|
|
|
|
const propDefs = new Map();
|
|
|
|
|
|
|
|
for (let property in definition.properties) {
|
|
|
|
const propertyDef = definition.properties[property];
|
|
|
|
|
|
|
|
propDefs.set(property, propertyDef);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let property of propList) {
|
|
|
|
const propertyDef = propDefs.get(property);
|
|
|
|
|
|
|
|
if (!propertyDef) return;
|
|
|
|
|
|
|
|
if (propertyDef.log === false)
|
|
|
|
delete properties[property];
|
|
|
|
else if (propertyDef.logValue === false)
|
|
|
|
properties[property] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
// this function retuns all the instances changed in case this is an updateAll
|
2018-10-22 12:35:21 +00:00
|
|
|
function setLogsToSave(changedInstances, changedInstancesIds, logRecord, ctx) {
|
2018-10-18 09:11:17 +00:00
|
|
|
let promises = [];
|
2019-02-20 15:30:46 +00:00
|
|
|
if (changedInstances && typeof changedInstances == 'object') {
|
2018-10-18 09:11:17 +00:00
|
|
|
for (let i = 0; i < changedInstances.length; i++) {
|
|
|
|
logRecord.changedModelId = changedInstancesIds[i];
|
|
|
|
logRecord.changedModelValue = changedInstances[i];
|
2018-10-22 12:35:21 +00:00
|
|
|
if (ctx.oldInstances)
|
|
|
|
logRecord.oldInstance = ctx.oldInstances[i];
|
2018-10-18 09:11:17 +00:00
|
|
|
promises.push(JSON.parse(JSON.stringify(logRecord)));
|
|
|
|
}
|
2019-02-20 15:30:46 +00:00
|
|
|
} else
|
2018-10-18 09:11:17 +00:00
|
|
|
return logRecord;
|
2019-02-20 15:30:46 +00:00
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
return promises;
|
2018-10-03 07:37:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function setActionType(ctx) {
|
2021-03-19 18:20:19 +00:00
|
|
|
const oldInstance = ctx.hookState.oldInstance;
|
|
|
|
const newInstance = ctx.hookState.newInstance;
|
2018-10-03 07:37:34 +00:00
|
|
|
|
2019-02-20 15:30:46 +00:00
|
|
|
if (oldInstance && newInstance)
|
2018-10-03 07:37:34 +00:00
|
|
|
return 'update';
|
2019-02-20 15:30:46 +00:00
|
|
|
else if (!oldInstance && newInstance)
|
2018-10-03 07:37:34 +00:00
|
|
|
return 'insert';
|
2019-02-20 15:30:46 +00:00
|
|
|
|
2018-10-18 09:11:17 +00:00
|
|
|
return 'delete';
|
2018-10-03 07:37:34 +00:00
|
|
|
}
|
|
|
|
};
|