mongocat
Version:
✨ Mongocat 😺 is easy to use, configuration based Denormalization mongoose plugin for read heavy applications. Mongocat reduces write complexity too.
1,298 lines (1,071 loc) • 107 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var mongoose = require('mongoose');
function _regeneratorRuntime() {
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
_regeneratorRuntime = function () {
return exports;
};
var exports = {},
Op = Object.prototype,
hasOwn = Op.hasOwnProperty,
$Symbol = "function" == typeof Symbol ? Symbol : {},
iteratorSymbol = $Symbol.iterator || "@@iterator",
asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator",
toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
function define(obj, key, value) {
return Object.defineProperty(obj, key, {
value: value,
enumerable: !0,
configurable: !0,
writable: !0
}), obj[key];
}
try {
define({}, "");
} catch (err) {
define = function (obj, key, value) {
return obj[key] = value;
};
}
function wrap(innerFn, outerFn, self, tryLocsList) {
var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator,
generator = Object.create(protoGenerator.prototype),
context = new Context(tryLocsList || []);
return generator._invoke = function (innerFn, self, context) {
var state = "suspendedStart";
return function (method, arg) {
if ("executing" === state) throw new Error("Generator is already running");
if ("completed" === state) {
if ("throw" === method) throw arg;
return doneResult();
}
for (context.method = method, context.arg = arg;;) {
var delegate = context.delegate;
if (delegate) {
var delegateResult = maybeInvokeDelegate(delegate, context);
if (delegateResult) {
if (delegateResult === ContinueSentinel) continue;
return delegateResult;
}
}
if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) {
if ("suspendedStart" === state) throw state = "completed", context.arg;
context.dispatchException(context.arg);
} else "return" === context.method && context.abrupt("return", context.arg);
state = "executing";
var record = tryCatch(innerFn, self, context);
if ("normal" === record.type) {
if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue;
return {
value: record.arg,
done: context.done
};
}
"throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg);
}
};
}(innerFn, self, context), generator;
}
function tryCatch(fn, obj, arg) {
try {
return {
type: "normal",
arg: fn.call(obj, arg)
};
} catch (err) {
return {
type: "throw",
arg: err
};
}
}
exports.wrap = wrap;
var ContinueSentinel = {};
function Generator() {}
function GeneratorFunction() {}
function GeneratorFunctionPrototype() {}
var IteratorPrototype = {};
define(IteratorPrototype, iteratorSymbol, function () {
return this;
});
var getProto = Object.getPrototypeOf,
NativeIteratorPrototype = getProto && getProto(getProto(values([])));
NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype);
var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
function defineIteratorMethods(prototype) {
["next", "throw", "return"].forEach(function (method) {
define(prototype, method, function (arg) {
return this._invoke(method, arg);
});
});
}
function AsyncIterator(generator, PromiseImpl) {
function invoke(method, arg, resolve, reject) {
var record = tryCatch(generator[method], generator, arg);
if ("throw" !== record.type) {
var result = record.arg,
value = result.value;
return value && "object" == typeof value && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) {
invoke("next", value, resolve, reject);
}, function (err) {
invoke("throw", err, resolve, reject);
}) : PromiseImpl.resolve(value).then(function (unwrapped) {
result.value = unwrapped, resolve(result);
}, function (error) {
return invoke("throw", error, resolve, reject);
});
}
reject(record.arg);
}
var previousPromise;
this._invoke = function (method, arg) {
function callInvokeWithMethodAndArg() {
return new PromiseImpl(function (resolve, reject) {
invoke(method, arg, resolve, reject);
});
}
return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
};
}
function maybeInvokeDelegate(delegate, context) {
var method = delegate.iterator[context.method];
if (undefined === method) {
if (context.delegate = null, "throw" === context.method) {
if (delegate.iterator.return && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method)) return ContinueSentinel;
context.method = "throw", context.arg = new TypeError("The iterator does not provide a 'throw' method");
}
return ContinueSentinel;
}
var record = tryCatch(method, delegate.iterator, context.arg);
if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel;
var info = record.arg;
return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel);
}
function pushTryEntry(locs) {
var entry = {
tryLoc: locs[0]
};
1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry);
}
function resetTryEntry(entry) {
var record = entry.completion || {};
record.type = "normal", delete record.arg, entry.completion = record;
}
function Context(tryLocsList) {
this.tryEntries = [{
tryLoc: "root"
}], tryLocsList.forEach(pushTryEntry, this), this.reset(!0);
}
function values(iterable) {
if (iterable) {
var iteratorMethod = iterable[iteratorSymbol];
if (iteratorMethod) return iteratorMethod.call(iterable);
if ("function" == typeof iterable.next) return iterable;
if (!isNaN(iterable.length)) {
var i = -1,
next = function next() {
for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next;
return next.value = undefined, next.done = !0, next;
};
return next.next = next;
}
}
return {
next: doneResult
};
}
function doneResult() {
return {
value: undefined,
done: !0
};
}
return GeneratorFunction.prototype = GeneratorFunctionPrototype, define(Gp, "constructor", GeneratorFunctionPrototype), define(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) {
var ctor = "function" == typeof genFun && genFun.constructor;
return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name));
}, exports.mark = function (genFun) {
return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun;
}, exports.awrap = function (arg) {
return {
__await: arg
};
}, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () {
return this;
}), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
void 0 === PromiseImpl && (PromiseImpl = Promise);
var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) {
return result.done ? result.value : iter.next();
});
}, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () {
return this;
}), define(Gp, "toString", function () {
return "[object Generator]";
}), exports.keys = function (object) {
var keys = [];
for (var key in object) keys.push(key);
return keys.reverse(), function next() {
for (; keys.length;) {
var key = keys.pop();
if (key in object) return next.value = key, next.done = !1, next;
}
return next.done = !0, next;
};
}, exports.values = values, Context.prototype = {
constructor: Context,
reset: function (skipTempReset) {
if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined);
},
stop: function () {
this.done = !0;
var rootRecord = this.tryEntries[0].completion;
if ("throw" === rootRecord.type) throw rootRecord.arg;
return this.rval;
},
dispatchException: function (exception) {
if (this.done) throw exception;
var context = this;
function handle(loc, caught) {
return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught;
}
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i],
record = entry.completion;
if ("root" === entry.tryLoc) return handle("end");
if (entry.tryLoc <= this.prev) {
var hasCatch = hasOwn.call(entry, "catchLoc"),
hasFinally = hasOwn.call(entry, "finallyLoc");
if (hasCatch && hasFinally) {
if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);
if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);
} else if (hasCatch) {
if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);
} else {
if (!hasFinally) throw new Error("try statement without catch or finally");
if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);
}
}
}
},
abrupt: function (type, arg) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
var finallyEntry = entry;
break;
}
}
finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null);
var record = finallyEntry ? finallyEntry.completion : {};
return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record);
},
complete: function (record, afterLoc) {
if ("throw" === record.type) throw record.arg;
return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel;
},
finish: function (finallyLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel;
}
},
catch: function (tryLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc === tryLoc) {
var record = entry.completion;
if ("throw" === record.type) {
var thrown = record.arg;
resetTryEntry(entry);
}
return thrown;
}
}
throw new Error("illegal catch attempt");
},
delegateYield: function (iterable, resultName, nextLoc) {
return this.delegate = {
iterator: values(iterable),
resultName: resultName,
nextLoc: nextLoc
}, "next" === this.method && (this.arg = undefined), ContinueSentinel;
}
}, exports;
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
/**
* It's used on ProviderModel
* @param options = {
keyFields: ['_id','name','status],
ignoredFields: ['_id','name','status],
toRef: 'Category',
}
* @returns
*/
var provider = function provider(ops) {
var options = ops || {};
options.keyFields = options.keyFields || ['_id'];
options.ignoredFields = [].concat(options.ignoredFields || [], ['createdAt', 'updatedAt', '__v']);
return function (schema) {
var toRef = options.toRef,
ignoredFields = options.ignoredFields,
keyFields = options.keyFields; // for: return genre.save();
// eslint-disable-next-line @typescript-eslint/ban-types
schema.post(['save'], /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(doc, next) {
var modelRef;
return _regeneratorRuntime().wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!this['$__']['inserting']) {
// when provider updates then publish only otherwise no need
modelRef = toRef || this.constructor.modelName;
denormalizeEmitter.emit("denormalize/publish/" + modelRef, doc._doc);
}
return _context.abrupt("return", next());
case 2:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
return function (_x, _x2) {
return _ref.apply(this, arguments);
};
}());
schema.post(Object.keys(updateQueryMethods), /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
var self, providerModelRef, queryParams, updateParams, updateFields, listeningFields, listeningUpdateFields, key, keyFieldsFound, getKeyArray, operation, document;
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
self = this; // toRef
providerModelRef = self.model.modelName;
queryParams = this.getQuery();
updateParams = this.getUpdate();
updateFields = updateParams.$set || {};
listeningFields = Object.keys(updateFields).filter(function (v) {
return !(ignoredFields != null && ignoredFields.includes(v));
});
if (!(listeningFields.length === 0)) {
_context2.next = 8;
break;
}
return _context2.abrupt("return");
case 8:
listeningUpdateFields = {};
for (key in updateFields) {
if (listeningFields.includes(key)) {
listeningUpdateFields[key] = updateFields[key];
}
}
keyFieldsFound = Object.keys(queryParams).filter(function (v) {
return !Array.isArray(keyFields) || keyFields.includes(v);
});
getKeyArray = [queryParams];
if (!(keyFieldsFound.length !== (keyFields == null ? void 0 : keyFields.length))) {
_context2.next = 20;
break;
}
operation = self.op;
_context2.next = 16;
return self.model[updateQueryMethods[operation]](queryParams);
case 16:
document = _context2.sent;
if (!(!document || document.length === 0)) {
_context2.next = 19;
break;
}
throw new Error('Document not found');
case 19:
getKeyArray = Array.isArray(document) ? document : [document];
case 20:
getKeyArray.forEach(function (getKeyObj) {
var keyFieldsObj = keyFields && keyFields.reduce(function (acc, v) {
var _extends2;
return _extends({}, acc, (_extends2 = {}, _extends2[v] = getKeyObj[v], _extends2));
}, {});
var publishableData = _extends({}, listeningUpdateFields, keyFieldsObj); // const shouldDenormalize = !!self.options['denormalize']; // {denormalize: true}
var shouldDenormalize = self.options['denormalize'] || false;
self['$denormalize'] = shouldDenormalize;
if (!shouldDenormalize) return;
console.log('publish these fields', publishableData);
denormalizeEmitter.emit("denormalize/publish/" + providerModelRef, publishableData); // publish && publish(publishableData);
});
case 21:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
})));
};
};
/**
* It's used on sub schema on ConsumerModel
* @param options = {
key: '_id',
toPath: 'category', // no neccessary when we need duplicate with other collection
fromRef: 'Category',
toRef: 'Blog',
withTimestamp: true,
}
*/
var consumer = function consumer(ops) {
var options = ops || {};
options.toPath = options.toPath || options.fromRef.toLowerCase();
return function (schema) {
var _options$key = options.key,
key = _options$key === void 0 ? '_id' : _options$key,
fromRef = options.fromRef,
toRef = options.toRef,
withTimestamp = options.withTimestamp;
if (!key) return;
if (withTimestamp) {
schema.add({
denormalizedAt: {
type: Date
}
});
} // eslint-disable-next-line @typescript-eslint/ban-types
schema.pre(['save'], /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(next) {
var _ProviderModel$findOn, foreignKey, ProviderModel, provider;
return _regeneratorRuntime().wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!this) {
_context.next = 11;
break;
}
console.log({
plugin: 'consumer',
method: 'pre save',
provider: fromRef,
consumer: toRef
});
foreignKey = key;
ProviderModel = mongoose.model(fromRef);
_context.next = 6;
return ProviderModel.findOne((_ProviderModel$findOn = {}, _ProviderModel$findOn[foreignKey] = this[foreignKey], _ProviderModel$findOn), {
createdAt: 0,
updatedAt: 0
});
case 6:
provider = _context.sent;
if (provider) {
_context.next = 9;
break;
}
return _context.abrupt("return", next(new Error("Denormalization failed for key: " + key + ", value: " + this[key] + ", plugin: consumer ,method: save, consumer: " + toRef + ", provider: " + fromRef)));
case 9:
Object.assign(this, provider == null ? void 0 : provider.toObject());
next();
case 11:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}());
};
};
// object as parameter and returns the
// flatten object
var flattenObj = function flattenObj(ob) {
// The object which contains the
// final result
var result = {}; // loop through the object "ob"
for (var i in ob) {
// We check the type of the i using
// typeof() function and recursively
// call the function again
if (!(ob[i] instanceof mongoose.Types.ObjectId) && typeof ob[i] === 'object' && !Array.isArray(ob[i])) {
var temp = flattenObj(ob[i]);
for (var j in temp) {
// Store temp in result
result[i + '.' + j] = temp[j];
}
} // Else store ob[i] in result directly
else {
result[i] = ob[i];
}
}
return result;
};
var ObjectBuilder = /*#__PURE__*/function () {
function ObjectBuilder() {}
ObjectBuilder.filterObject = function filterObject(obj, condition, objCondition) {
var _this = this;
if (objCondition === void 0) {
objCondition = Object;
}
return Object.entries(obj).filter(function (_ref) {
var key = _ref[0],
value = _ref[1];
return condition(value, key);
}).reduce(function (acc, _ref2) {
var _extends2;
var key = _ref2[0],
value = _ref2[1];
return _extends({}, acc, (_extends2 = {}, _extends2[key] = Array.isArray(value) ? value : value === objCondition(value) ? _this.filterObject(value, condition) : value, _extends2));
}, {});
};
return ObjectBuilder;
}();
/**
* It's used on ConsumerModel
* @param options = {
key: '_id',
fromRef: 'Category', // make default: toPath = category
inArray: true // if it's like genres
}
* @returns
*/
var watchConsumer = function watchConsumer(ops) {
var options = ops || {};
options.toPath = options.toPath || options.fromRef.toLowerCase();
return function (schema) {
var toPath = options.toPath,
_options$key = options.key,
key = _options$key === void 0 ? '_id' : _options$key,
fromRef = options.fromRef,
_options$exceptionOnF = options.exceptionOnFailure,
exceptionOnFailure = _options$exceptionOnF === void 0 ? false : _options$exceptionOnF,
toRef = options.toRef,
_options$inArray = options.inArray,
inArray = _options$inArray === void 0 ? false : _options$inArray,
_options$strict = options.strict,
strict = _options$strict === void 0 ? false : _options$strict;
if (!toPath) return;
/***
* Event emit after provider publishing
*/
denormalizeEmitter.on("denormalize/publish/" + fromRef, /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(data) {
var _filterQuery;
var ConsumerModel, _docData, flattenPublishedData, schemaFeilds, filteredFlattenedData, filterKey, filterQuery, updateFlattenData;
return _regeneratorRuntime().wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
// Step-1: Get Model instance
// const ProviderModel = model(options.fromRef);
ConsumerModel = mongoose.model(options.toRef); // Step-2: Get updatable key from Provider
_docData = data != null && data._doc ? data == null ? void 0 : data._doc : data;
flattenPublishedData = _docData; // Step-3: Get updatable key on consumer
schemaFeilds = Object.keys(schema.paths);
console.log({
flattenPublishedData: flattenPublishedData
});
filteredFlattenedData = ObjectBuilder.filterObject(flattenPublishedData, function (_, k) {
return schemaFeilds.includes(k);
}, function (value) {
return !(value instanceof mongoose.Types.ObjectId) && Object(value);
}); //TODO: get denormable keys first like GQL
// Step-4: Update ConsumerModel
filterKey = toPath + "." + key;
filterQuery = (_filterQuery = {}, _filterQuery[filterKey] = data[key], _filterQuery);
updateFlattenData = Object.entries(filteredFlattenedData).reduce(function (acc, _ref2) {
var _extends2;
var k = _ref2[0],
value = _ref2[1];
var consumerUpdateKeyPath = inArray ? toPath + ".$." + k : toPath + "." + k;
return _extends({}, acc, (_extends2 = {}, _extends2[consumerUpdateKeyPath] = value, _extends2));
}, {});
_context.next = 11;
return ConsumerModel.updateMany(filterQuery, updateFlattenData, {
"new": true
});
case 11:
console.info('\x1b[47m\x1b[46m%s\x1b[0m', "\u2728 Successfully denormalized via hook watchConsumer event emitted \u2728", '\x1b[1m\x1b[4m', "toRef: " + toRef + ", from provider: " + fromRef + ", toPath: " + toPath + ", inArray: " + inArray + " \uD83D\uDC26");
case 12:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}());
/***
* Event emit after provider publishing
*/
if (fromRef && options.changeStreamEnabled) {
console.log("CHANGE STREAM: " + options.toRef + " > " + toPath + " << provider: " + options.fromRef);
process.nextTick(function () {
var ConsumerModel = mongoose.model(options.toRef);
var ProviderModel = mongoose.model(options.fromRef);
var cursor;
try {
//FIXME: test it using local unsupported DB
cursor = ProviderModel.watch();
} catch (error) {
cursor = null;
throw new Error("Change Stream not supported");
} // ChangeStreamDocument<any>
if (cursor) {
cursor.on('change', /*#__PURE__*/function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(change) {
var _change$updateDescrip, updatedData, _schema$paths$toPath, _schema$paths$toPath$, _change$documentKey, matchedKeys, updateDenormedData, flattenProvider, providerId, _contentQuery, _flattenObj, contentQuery, contentUpdateBody, _schema$paths$toPath2, _schema$paths$toPath3, _change$documentKey2, _matchedKeys, _updateDenormedData, _flattenProvider, _providerId, _contentQuery3, _flattenObj2, _contentQuery2, _contentUpdateBody;
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
console.log(options.fromRef + " is called for " + options.toRef);
if (!(change.operationType === 'update')) {
_context2.next = 49;
break;
}
updatedData = ((_change$updateDescrip = change.updateDescription) == null ? void 0 : _change$updateDescrip.updatedFields) || {};
if (!options.inArray) {
_context2.next = 27;
break;
}
matchedKeys = Object.keys((_schema$paths$toPath = schema.paths[toPath]) == null ? void 0 : (_schema$paths$toPath$ = _schema$paths$toPath.schema) == null ? void 0 : _schema$paths$toPath$.paths);
updateDenormedData = {};
flattenProvider = flattenObj(updatedData);
matchedKeys.forEach(function (key) {
updateDenormedData[key] = flattenProvider[key];
}); // const providerId: any = change.documentKey?._id as unknown as any;
providerId = (_change$documentKey = change.documentKey) == null ? void 0 : _change$documentKey._id;
if (providerId) {
_context2.next = 11;
break;
}
return _context2.abrupt("return");
case 11:
_context2.prev = 11;
contentQuery = (_contentQuery = {}, _contentQuery[toPath + "." + key] = providerId, _contentQuery);
contentUpdateBody = flattenObj((_flattenObj = {}, _flattenObj[toPath + ".$"] = updateDenormedData, _flattenObj));
console.log(contentQuery, contentUpdateBody);
_context2.next = 17;
return ConsumerModel.updateMany(contentQuery, contentUpdateBody);
case 17:
console.info('\x1b[47m\x1b[46m%s\x1b[0m', "\u2728 Successfully denormalized from changeStream \u2728", "toRef: " + options.toRef + ", from provider: " + options.fromRef + ", toPath: " + options.toPath + " \uD83D\uDC26"); // CALL AFTER UPDATE HOOK
_context2.next = 25;
break;
case 20:
_context2.prev = 20;
_context2.t0 = _context2["catch"](11);
console.log("Failed to denormalize error by changeStream on mongoose, Model:" + options.toRef + ",Field: " + options.toPath, _context2.t0.message);
_context2.next = 25;
return cursor.close();
case 25:
_context2.next = 49;
break;
case 27:
if (options.inArray) {
_context2.next = 49;
break;
}
_matchedKeys = Object.keys((_schema$paths$toPath2 = schema.paths[toPath]) == null ? void 0 : (_schema$paths$toPath3 = _schema$paths$toPath2.schema) == null ? void 0 : _schema$paths$toPath3.paths);
_updateDenormedData = {};
_flattenProvider = flattenObj(updatedData);
_matchedKeys.forEach(function (key) {
_updateDenormedData[key] = _flattenProvider[key];
}); // const providerId: any = change.documentKey?._id as unknown as any;
_providerId = (_change$documentKey2 = change.documentKey) == null ? void 0 : _change$documentKey2._id;
if (_providerId) {
_context2.next = 35;
break;
}
return _context2.abrupt("return");
case 35:
_context2.prev = 35;
_contentQuery2 = (_contentQuery3 = {}, _contentQuery3[toPath + "." + key] = _providerId, _contentQuery3);
_contentUpdateBody = flattenObj((_flattenObj2 = {}, _flattenObj2[toPath] = _updateDenormedData, _flattenObj2));
console.log(_contentQuery2, _contentUpdateBody);
_context2.next = 41;
return ConsumerModel.updateMany(_contentQuery2, _contentUpdateBody);
case 41:
// CALL AFTER UPDATE HOOK
console.info('\x1b[47m\x1b[46m%s\x1b[0m', "\u2728 Successfully denormalized from changeStream\u2728", "toRef: " + options.toRef + ", from provider: " + options.fromRef + ", toPath: " + options.toPath + " \uD83D\uDC26");
_context2.next = 49;
break;
case 44:
_context2.prev = 44;
_context2.t1 = _context2["catch"](35);
console.log("Failed to denormalize error by changeStream on mongoose, Model:" + options.toRef + ",Field: " + options.toPath, _context2.t1.message);
_context2.next = 49;
return cursor.close();
case 49:
case "end":
return _context2.stop();
}
}
}, _callee2, null, [[11, 20], [35, 44]]);
}));
return function (_x2) {
return _ref3.apply(this, arguments);
};
}());
cursor.on('error', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
_context3.next = 2;
return cursor.close();
case 2:
console.log("Change Stream not supported");
case 3:
case "end":
return _context3.stop();
}
}
}, _callee3);
})));
}
});
} // eslint-disable-next-line @typescript-eslint/ban-types
schema.pre(['save'], /*#__PURE__*/function () {
var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(next) {
var doc, msg, _filterQuery2, ProviderModel, filterQuery, provider, _doc$toPath, _doc$toPath$filter, _ProviderModel$find, groupArray, _ProviderModel, providers;
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
// eslint-disable-next-line @typescript-eslint/no-this-alias
doc = this; // if (!this[toPath] || !this[relationship as string] || !doc[toPath][key]) return;
// next(new Error(`to path nai`));
if (!((this.isNew || this.isModified(toPath)) && this[toPath])) {
_context4.next = 43;
break;
}
msg = {
plugin: 'watchConsumer',
method: 'pre save',
provider: fromRef,
consumer: toRef,
inArray: inArray ? 'array' : 'object',
doc: doc,
isNew: this.isNew ? 'creating' : 'updating',
modification: toPath + " " + (this.isModified(toPath) ? 'is ' : 'is not') + " modified"
};
console.log(msg);
if (!(!inArray && this[toPath] && this[toPath][key])) {
_context4.next = 20;
break;
}
// Step-2: get provided data;
ProviderModel = mongoose.model(fromRef);
filterQuery = (_filterQuery2 = {}, _filterQuery2[key] = this[toPath][key], _filterQuery2);
_context4.next = 9;
return ProviderModel.findOne(filterQuery, {
createdAt: 0,
updatedAt: 0
});
case 9:
provider = _context4.sent;
if (provider) {
_context4.next = 16;
break;
}
if (!(strict || exceptionOnFailure)) {
_context4.next = 15;
break;
}
return _context4.abrupt("return", next(new Error("Denormalization failed for key: " + key + ", value: " + this[toPath][key] + ", plugin: watchConsumer, method: save, consumer: " + toPath + ", provider: " + fromRef)));
case 15:
return _context4.abrupt("return", next());
case 16:
// Step-3: set denormmalized value on field on watchConsumer
Object.assign(this[toPath], provider == null ? void 0 : provider.toObject());
return _context4.abrupt("return", next());
case 20:
if (!(inArray && this[toPath])) {
_context4.next = 42;
break;
}
groupArray = (_doc$toPath = doc[toPath]) == null ? void 0 : (_doc$toPath$filter = _doc$toPath.filter(function (data) {
return data[key];
})) == null ? void 0 : _doc$toPath$filter.map(function (value) {
return value[key];
});
if (!(!this[toPath] || !groupArray || !groupArray.length)) {
_context4.next = 29;
break;
}
delete this[toPath]; // I even don't what will be it's effect
if (!(strict || exceptionOnFailure)) {
_context4.next = 28;
break;
}
return _context4.abrupt("return", next(new Error("Denormalization failed for feild: " + toPath + ", array: " + groupArray + ", plugin: watchConsumer, method: save, consumer: " + toRef + ", provider: " + fromRef)));
case 28:
return _context4.abrupt("return", next());
case 29:
console.log('plugin: watchConsumer , method: update on: inArray ');
_ProviderModel = mongoose.model(fromRef);
/**
* FIXME: if change consumer sub schema then fetch from provider
* else DO NOTHING
*/
_context4.next = 33;
return _ProviderModel.find((_ProviderModel$find = {}, _ProviderModel$find[key] = {
$in: groupArray
}, _ProviderModel$find));
case 33:
providers = _context4.sent;
if (providers.length) {
_context4.next = 40;
break;
}
if (!(strict || exceptionOnFailure)) {
_context4.next = 39;
break;
}
return _context4.abrupt("return", next(new Error("Denormalization failed for feild: " + toPath + ", array: " + groupArray + ", plugin: watchConsumer, method: save, consumer: " + toRef + ", provider: " + fromRef)));
case 39:
return _context4.abrupt("return", next());
case 40:
/**
* Consumer Update
* { _id: 62960300da98cc1aba3e9ff2 } // doc['_id']
* { users: [
{
* _id: 62960300da98cc1aba3e9ff2,
* name: Samayun Chowdhury
* username: samayunchy
* },
* {
* _id: 62a9c113382dec8e79c02178,
* name: AzadChowdhury
* username: azad
* }
* ]}
*/
// Step-3: set denormmalized value on field on watchConsumer
Object.assign(this[toPath], providers);
return _context4.abrupt("return");
case 42:
return _context4.abrupt("return", next());
case 43:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
}));
return function (_x3) {
return _ref5.apply(this, arguments);
};
}()); // eslint-disable-next-line @typescript-eslint/ban-types
// schema.post(['save'], async function (doc: Record<string, any>, next: Function) {
// console.log(`post hook on watchConsumer`, this.isModified(doc[toPath]));
// next();
// });
/**
* VALIDATE CONSUMER UPDATE FIELD
*/
schema.pre(Object.keys(updateQueryMethods), /*#__PURE__*/function () {
var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(next) {
var self, shouldDenormalize, updateParams, doc, ignoredFields, denormableFeildsPrimaryKey, denormableFeildsPrimaryValue, _ProviderModel$findOn, ProviderModel, provider, _updateFields$filter, _ProviderModel2$find, updateFields, groupArray, _ProviderModel2, providers;
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
// eslint-disable-next-line @typescript-eslint/no-this-alias
self = this;
if (self) {
_context5.next = 3;
break;
}
return _context5.abrupt("return");
case 3:
// const shouldDenormalize = !!self.options['denormalize']; // {denormalize: true}
shouldDenormalize = !self.options['denormalize'] ? self.options['denormalize'] : true;
self['$denormalize'] = shouldDenormalize;
if (shouldDenormalize) {
_context5.next = 7;
break;
}
return _context5.abrupt("return");
case 7:
updateParams = self.getUpdate();
doc = updateParams;
if (doc) {
_context5.next = 11;
break;
}
return _context5.abrupt("return");
case 11:
self['$denormalize'] = shouldDenormalize; // const updateField = updateParams.$set || {};
ignoredFields = ['createdAt', 'updatedAt', '__v'];
ignoredFields.forEach(function (key) {
delete updateParams[key];
});
delete doc['$set'];
delete doc['$setOnInsert'];
if (!(!inArray && shouldDenormalize && !!doc && !!doc[toPath])) {
_context5.next = 37;
break;
}
denormableFeildsPrimaryKey = toPath + "." + key;
denormableFeildsPrimaryValue = doc[denormableFeildsPrimaryKey] ? doc[denormableFeildsPrimaryKey] : doc[toPath][key];
if (!(!!doc[toPath] && !!doc[toPath][key])) {
_context5.next = 34;
break;
}
console.log({
plugin: 'watchConsumer',
method: 'pre findOneAndUpdate',
provider: fromRef,
consumer: toRef,
inArray: inArray,
toPath: toPath,
key: key,
doc: doc
});
ProviderModel = mongoose.model(fromRef);
_context5.next = 24;
return ProviderModel.findOne((_ProviderModel$findOn = {}, _ProviderModel$findOn[key] = denormableFeildsPrimaryValue, _ProviderModel$findOn));
case 24:
provider = _context5.sent;
if (provider) {
_context5.next = 31;
break;
}
if (!(strict || exceptionOnFailure)) {
_context5.next = 30;
break;
}
return _context5.abrupt("return", next(new Error("Denormalization failed for toPath:" + toPath + ", value:" + denormableFeildsPrimaryValue + " plugin: watchConsumer, method: findOneAndUpdate on: !inArray")));
case 30:
return _context5.abrupt("return", next());
case 31:
console.log('\x1b[47m\x1b[46m%s\x1b[0m', "Denormalizing on feild: " + toPath + " , queryKey: " + denormableFeildsPrimaryKey + " bodyValue: " + denormableFeildsPrimaryValue);
/**
* Consumer Update
* { user._id: 62960300da98cc1aba3e9ff2 }
* { user: {
* _id: 62960300da98cc1aba3e9ff2,
* name: Samayun Chowdhury
* username: samayunchy
* }}
*/
// await this.model.updateMany(
// { [denormableFeildsPrimaryKey]: denormableFeildsPrimaryValue },
// { [toPath as string]: provider?.toObject() },
// );
// Object.assign(doc[toPath], provider?.toObject());
doc[toPath] = provider == null ? void 0 : provider.toObject();
return _context5.abrupt("return", next());
case 34:
return _context5.abrupt("return", next());
case 37:
if (!(inArray && shouldDenormalize && !!doc && !!doc[toPath])) {
_context5.next = 59;
break;
}
console.log({
plugin: 'watchConsumer',
method: 'pre findOneAndUpdate',
provider: fromRef,
consumer: toRef,
toPath: toPath
});
console.log("plugin: watchConsumer, method: findOneAndUpdate TYPE: PRE HOOK on: inArray path: " + toPath + " data: " + updateParams[toPath]);
console.log({
updateParams: updateParams,
'updateParams[toPath]': updateParams[toPath]
});
updateFields = updateParams[toPath] || [];
if (!(!updateFields || !updateFields.length)) {
_context5.next = 44;
break;
}
return _context5.abrupt("return", next());
case 44:
groupArray = updateFields == null ? void 0 : (_updateFields$filter = updateFields.filter(function (data) {
return data[key];
})) == null ? void 0 : _updateFields$filter.map(function (value) {
return value[key];
});
if (groupArray.length) {
_context5.next = 47;
break;
}
return _context5.abrupt("return", next(new Error("Denormalization failed on watchConsumer inArray portion for " + key + " array: " + groupArray + ". WRONG KEY INPUT FROM FRONTEND")));
case 47:
_ProviderModel2 = mongoose.model(fromRef);
_context5.next = 50;
return _ProviderModel2.find((_ProviderModel2$find = {}, _ProviderModel2$find[key] = {
$in: groupArray
}, _ProviderModel2$find));
case 50:
providers = _context5.sent;
if (!(!providers || !providers.length)) {
_context5.next = 57;
break;
}
if (!(strict || exceptionOnFailure)) {
_context5.next = 56;
break;
}
return _context5.abrupt("return", next(new Error("Denormalization failed for feild: " + toPath + ", array: " + groupArray + ", plugin: watchConsumer, method: findOneAndUpdate, provider: " + fromRef)));
case 56:
return _context5.abrupt("return", next());
case 57:
// Object.assign(doc[toPath], providers);
doc[toPath] = providers;
return _context5.abrupt("return", next());
case 59:
return _context5.abrupt("return", next());
case 60:
case "end":
retur