节点中的Decorator-是否可以在构造函数内迭代类方法,以便覆盖/应用Decorator



我是Node/Javascript an的新手。我想做的是使用decorator函数将日志记录添加到我的存储库中。因此,我试图从构造函数内部迭代每个函数,并用类似的东西覆盖它:">
Object.getOwnPropertyNames(Repository.protype(.forEach((func(=>this.decorator(func(("我的问题是"Object.getOwnPropertyNames"只返回函数名称,而不是实际函数。有没有一种方法可以将这个装饰器应用于每个函数?

"use strict"
const db = require("./Database/db_operations");
const logger = require("./utils/logger")
const {createTables} = require("./Database/db_operations");
const loggingTypes = require("./utils/logginTypes")
class Repository {
async saveTermin(Termin) {
}

async saveToDo(toDo) {
return await db.saveToDo(toDo);
}
async saveAppointment(Appointment) {
return await db.saveAppointment(Appointment);
}
async updateAppointment(Appointment) {
return await db.updateAppointment(Appointment);
}
async deleteAppointment(uuid) {
return await db.deleteAppointment(uuid);
}
async saveAppointmentParticipants(appointment) {
return await db.saveAppointmentParticipants(appointment);
}
async saveAppointmentFiles(appointment) {
return await db.saveAppointmentFiles(appointment)
}
async getAppointmentFiles(appointment) {
return await db.getAppointmentFiles(appointment)
}
async deleteToDo(todo) {
return await db.deleteToDo(todo)
}
}

// All functions will be mapped to there type to optimize logging. If a function is not mapped to its type,
// it will be automaticly assigned to the "unspecified type". Logging will still work, but depending on what
// arguments are given and what is returned, the output might not perfectly fit
const funcMapping = new Map();
// GET
funcMapping.set(Repository.prototype.getAppointmentFiles, loggingTypes.GET);
funcMapping.set(Repository.prototype.getAllDatas, loggingTypes.GET);
funcMapping.set(Repository.prototype.getAllToDos, loggingTypes.GET);

//SAVE
funcMapping.set(Repository.prototype.saveToDo, loggingTypes.SAVE);
funcMapping.set(Repository.prototype.saveAppointment, loggingTypes.SAVE);
funcMapping.set(Repository.prototype.saveAppointmentParticipants, loggingTypes.SAVE);
//DELETE
funcMapping.set(Repository.prototype.deleteAppointment, loggingTypes.DELETE);
funcMapping.set(Repository.prototype.deleteToDo, loggingTypes.DELETE);

Object.getOwnPropertyNames(Repository.prototype)
.forEach(name => {
const func = Repository.prototype[name];
// checking loggingTypes - if no type is assigned function will be assigned to "UNASSIGNED".
// console.log(funcMapping.has(func) +" "+ func.name)
if (!funcMapping.has(func)) {
funcMapping.set(func, loggingTypes.UNASSIGNED);
}
// function will only be wrapped if logging is enabled.
if (funcMapping.get(func)[1]) {
Repository.prototype[name] = async function (...args) {
// calls the original methode
const returnValue = await func.apply(this, args);
const argumentsInArray = Array.prototype.slice.call(args);
// Put any additional logic here and it will be applied -> magic
// Logging
db.writeLogging(logger(func, returnValue, funcMapping.get(func)[0]), args).then(() => {
console.log(`Function "${name}()" was successfully logged and saved to Database`)
}).catch(e => {
console.log(`Function "${name}()" could not be logged and saved to Database. ${func}`)
console.log(e)
})
return returnValue;
}
}
});
module.exports = new Repository();

const appointment_model = require('../models/Appointment');
const contact_model = require('../models/Contact');
const toDo_model = require('../models/ToDo');
const file_model = require('../models/File');
const loggingTypes = require("./logginTypes")


function log() {
// returns a function that returns an object. When this function is then called the object is returned
return function decorator(funcToLog, returnValue, funcType, ...args) {
// console.log("arguments in logger" + args);

// create prototype for object that later will be passed to database
const descriptor = function (user, change, changedAt) {
this.user = user; // some user id
this.change = change; //
this.changedAt = changedAt; // date when changes occoured
this.appointmentId = getUuid(appointment_model);
this.todoId = getUuid(toDo_model);
this.contactId = getUuid(contact_model);
this.fileId = getUuid(file_model);
};


// contains all logging Data about the function beeing called -> name of function, usedArguments and returnValue
function getChanges(func, funcType, returnValue, args) {
let changes = null;
switch (funcType) {
case loggingTypes.GET[0]:
changes = {
funcName: func.name, //
funcType: funcType, //
dataSetToChange: {...args},
newData: returnValue
}
break;
case loggingTypes.SAVE[0]:
changes = {
funcName: func.name, //
funcType: funcType, //
dataSetToChange: {...args}, // ?
newData: returnValue // could call function here
}
break;
case loggingTypes.UPDATE[0]:
changes = {
funcName: func.name, //
funcType: funcType, //
dataSetToChange: {...args},
newData: returnValue
}
break;
case loggingTypes.DELETE[0]:
changes = {
funcName: func.name, //
funcType: funcType, //
dataSetToChange: {...args},
newData: returnValue
}
break;
case loggingTypes.UNASSIGNED[0]:
changes = {
funcName: func.name, //
funcType: funcType, //
dataSetToChange: {...args},
newData: returnValue
}
}
return changes;
}

function getUuid(model_type) {
let uuid = null;
console.log(args)
for (let i = 0; i < args.length; i++) {
console.log(args[i])
if (args[i] instanceof model_type) {
uuid = parseInt(args[i].uuid);
}
return uuid;
}
}


return new descriptor("someUserId", JSON.stringify(getChanges(funcToLog, funcType, returnValue, args)), new Date())
}
}

module.exports = log();

您可以使用中间步骤轻松地将函数名称映射到它们的值:

Object.getOwnPropertyNames(Repository.prototype)
.map(name => Repository.prototype[name])
.forEach((func) => this.decorator(func));

无论如何,构造函数并不是最好的方法,因为每次创建类的新实例时都会应用decorator。

我宁愿在类定义之后,在分配给module.exports之前,移动整个decorator逻辑。

Object.getOwnPropertyNames(Repository.prototype)
.forEach(name => {
const func = Repository.prototype[name];
Repository.prototype[name] = function (...args) {
console.log("Decorator was called");
const returnValue = func.apply(this, args);
// Put additional logging logic here...
return returnValue;
}
});

更新

作为对评论中所述内容的回应,这里是上面代码的一个更健壮的版本,带有您可能需要或不需要的额外预防措施:

  • 保留非函数
  • 保留非值属性
  • 保留构造函数
  • 保留不可配置的属性
  • 包括带有符号键的特性
Reflect.ownKeys(Repository.prototype).forEach(key => {
const descriptor = Reflect.getOwnPropertyDescriptor(Repository.prototype, key);
if (!descriptor.configurable) return;
const { value } = descriptor;
if (typeof value !== 'function') return;
if (value === Repository) return;
descriptor.value = function (...args) {
console.log("Decorator was called");
const returnValue = value.apply(this, args);
// Additional logging logic here...
return returnValue;
};
Object.defineProperty(Repository.prototype, key, descriptor);
});

我遗漏的另一件事是额外的逻辑,以确保修饰的方法与原始函数具有相同的长度和名称属性以及相同的原型。当您在使用代码时发现额外的需求时,您可能需要调整更多的细节。

最新更新