import { Meteor } from 'meteor/meteor' import { isEqual, forOwn } from 'lodash'; import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills.js'; import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js'; import { ComputedOnlyEffectSchema } from '/imports/api/properties/Effects.js'; import { ComputedOnlyToggleSchema } from '/imports/api/properties/Toggles.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; const schemasByType = { 'skill': ComputedOnlySkillSchema, 'attribute': ComputedOnlyAttributeSchema, 'effect': ComputedOnlyEffectSchema, 'toggle': ComputedOnlyToggleSchema, }; export default function writeAlteredProperties(memo){ let bulkWriteOperations = []; // Loop through all properties on the memo forOwn(memo.propsById, changed => { let schema = schemasByType[changed.type]; if (!schema) return; let extraIds = changed.computationDetails.idsOfSameName; let ids; if (extraIds && extraIds.length){ ids = [changed._id, ...extraIds]; } else { ids = [changed._id]; } ids.forEach(id => { let op = undefined; let original = memo.originalPropsById[id]; op = addChangedKeysToOp(op, schema.objectKeys(), original, changed); if (op){ bulkWriteOperations.push(op); } }); }); bulkWriteProperties(bulkWriteOperations); } function addChangedKeysToOp(op, keys, original, changed) { // Loop through all keys that can be changed by computation // and compile an operation that sets all those keys for (let key of keys){ if (!isEqual(original[key], changed[key])){ if (!op) op = newOperation(original._id, changed.type); let value = changed[key]; if (value === undefined){ // Unset values that become undefined addUnsetOp(op, key); } else { // Set values that changed to something else addSetOp(op, key, value); } } } return op; } function newOperation(_id, type){ let newOp = { updateOne: { filter: {_id}, update: {}, } }; if (Meteor.isClient){ newOp.type = type; } return newOp; } function addSetOp(op, key, value){ if (op.updateOne.update.$set){ op.updateOne.update.$set[key] = value; } else { op.updateOne.update.$set = {[key]: value}; } } function addUnsetOp(op, key){ if (op.updateOne.update.$unset){ op.updateOne.update.$unset[key] = 1; } else { op.updateOne.update.$unset = {[key]: 1}; } } function bulkWriteProperties(bulkWriteOps){ if (!bulkWriteOps.length) return; if (Meteor.isServer){ CreatureProperties.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e){ if (e) { console.error('Bulk write failed: '); console.error(e); } } ); } else { bulkWriteOps.forEach(op => { let updateOneOrMany = op.updateOne || op.updateMany; CreatureProperties.update(updateOneOrMany.filter, updateOneOrMany.update, { // The server code is bypassing collection 2 validation, so do the same // on the client // include this if bypass is off: // selector: {type: op.type} bypassCollection2: true, }); }); } }