Better model pub/sub errors

This commit is contained in:
2026-02-25 19:30:54 -05:00
parent 585aa14c0b
commit 427e9411e7
+69 -33
View File
@@ -1,55 +1,91 @@
'use strict'; 'use strict';
/**
* PubSub controller dependency to handle message broadcasting
*/
const ps = require('../controller/pubsub'); const ps = require('../controller/pubsub');
/**
* Wraps a model in a Proxy to automatically publish events on specific method calls.
* @param {Object|Function} model - The data model or instance to be proxied.
* @returns {Proxy} - The proxied model.
*/
function ModelPs(model) {
// Ensure we have a reference to the class constructor regardless of whether an instance or class was passed
const Model = model.constructor.name === 'Function' ? model : model.constructor;
function ModelPs(model){ /**
const Model = model.constructor.name === 'Function' ? model : model.constructor * Extracts the unique identifier (primary key) from the model or request/response objects.
*/
function getIndex(req, res){ function getIndex(req, res) {
if(model[Model._key]) return model[Model._key]; if (model[Model._key]) return model[Model._key];
if(req && req[Model._key]) return req[Model._key]; if (req && req[Model._key]) return req[Model._key];
if(res && res[Model._key]) return res[Model._key]; if (res && res[Model._key]) return res[Model._key];
} }
function publish(prop, res, req){ /**
try{ * Formats and broadcasts the message via PubSub.
if(!['add', 'create', 'update', 'remove'].includes(prop)) return; * Topic format: model:ClassName:Action:ID
*/
function publish(prop, res, req) {
try {
// Only trigger for specific mutation keywords
if (!['add', 'create', 'update', 'remove'].includes(prop)) return;
ps.publish(`model:${Model.name}:${prop}:${getIndex(res, req)}`, res); ps.publish(`model:${Model.name}:${prop}:${getIndex(res, req)}`, res);
}catch(error){ } catch (error) {
console.log('ModelPs.publish ERROR', error) console.log('ModelPs.publish ERROR', error);
}
}
/**
* Standardized error logger that ignores common/non-critical HTTP errors
*/
function handleError(error, model, propKey) {
if (![401, 404, 429].includes(error.status)) {
console.error("Error PS", model.name, propKey, error);
} }
} }
return new Proxy(model, { return new Proxy(model, {
/**
* Intercepts 'new' keyword calls to ensure instances are also proxied.
*/
construct(target, args, newTarget) { construct(target, args, newTarget) {
return ModelPs(Reflect.construct(target, args, newTarget)) return ModelPs(Reflect.construct(target, args, newTarget));
}, },
get(target, propKey, receiver) {
if(propKey == 'constructor') return target.constructor;
const targetValue = Reflect.get(target, propKey, receiver);
if (typeof targetValue === 'function') {
return function(...args){
try{
// let res = targetValue.apply(this, args); // (A)
var res = Reflect.apply(targetValue, this, args);
if(targetValue.constructor.name === 'AsyncFunction'){
res.then(function(res){
publish(propKey, res, ...args);
}).catch(function(error){
console.error("Error async PS", model.name, propKey, error) /**
// console.log('toDo, publish errors...'); * Intercepts property/method access.
}); */
}else{ get(target, propKey, receiver) {
// Ensure constructor access remains direct
if (propKey == 'constructor') return target.constructor;
const targetValue = Reflect.get(target, propKey, receiver);
// If the property accessed is a function, wrap it to inject the PubSub logic
if (typeof targetValue === 'function') {
return function(...args) {
try {
// Execute the original method
var res = Reflect.apply(targetValue, this, args);
// Handle Asynchronous results (Promises)
if (targetValue.constructor.name === 'AsyncFunction') {
res.then(function(res) {
publish(propKey, res, ...args);
}).catch((error) => handleError(error, model, propKey));
} else {
// Handle Synchronous results
publish(propKey, res, ...args); publish(propKey, res, ...args);
} }
return res; return res;
}catch(error){ } catch (error) {
console.error("Error PS", model.name, propKey, error) handleError(error, model, propKey);
// console.log("toDo, publish errors...");
} }
} };
} else { } else {
return targetValue; return targetValue;
} }