Up front I'm new to Node / Javascript an. What I am trying to do is to add a logging to my repository using a decorator function. Therefor I'm trying to iterate though each function from inside the constructor and override it with something like: "
Object.getOwnPropertyNames(Repository.prototype).forEach((func) => this.decorator(func));"
My problem is that "Object.getOwnPropertyNames" only returns the function names instead of the actual function. Is there a way to apply this decorator to each function?
"use strict"
const db = require("./Database/db_operations");
const logger = require("./utils/logger")
const {createTables} = require("./Database/db_operations");
class Repository {
constructor() {
// Object.getOwnPropertyNames is not working since it only returns the function - names
// iterates through all Repository - methods to wrap them inside the decorator
Object.getOwnPropertyNames(Repository.prototype).forEach((func) => this.decorator(func));
}
//Returns wrapper object which contains all the added/decorated functionallity(functions are treated as objects).
decorator(func) {
console.log("Decorator was used")
return (...args) => {
console.log("Decorator was called");
// Executing function
const returnValue = func(...args);
// Logging
db.writeLogging(logger(func, returnValue, ...args)).then( ()=> {
console.log(`Accessing "${func}()" was successfully logged and saved to Database`)
}).catch(e => {
console.log(`Accessing "${func}()" could not be logged and saved to Database`)
console.log(e)
})
}
}
async createTables() {
console.log(db.createTables());
}
async safeStandort(Standort) {
return await db.safeStandort(Standort);
}
async safeAdresse(Adresse) {
await db.safeAdresse(Adresse);
}
async safeContact(Contact) {
return await db.safeContact(Contact);
}
async safeFile(File) {
}
async safeTermin(Termin) {
}
async safeToDo(toDo) {
return await db.safeToDo(toDo);
}
async safeAppointment(Appointment) {
return await db.safeAppointment(Appointment);
}
}
module.exports = new Repository();
CodePudding user response:
You can easily map function names to their values using an intermediate step:
Object.getOwnPropertyNames(Repository.prototype)
.map(name => Repository.prototype[name])
.forEach((func) => this.decorator(func));
Anyway, the constructor is not the best place to do this, because you would end up applying the decorator every time a new instance of the class is created.
I would rather move the whole decorator logic after the class definition, before the assignment to module.exports.
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;
}
});
Update
In response to what noted in the comments, here is a somewhat more robust version of the code above, with additional precautions you may or may not need:
- Preserve non-functions
- Preserve non-value properties
- Preserve the constructor
- Preserve non-configurable properties
- Include properties with symbol keys
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);
});
Another thing I left out is additional logic to make sure that the decorated methods have the same length and name properties and the same prototype as the original functions. You may want to adjust even more details as you discover additional requirements while using your code.
