UNPKG

@ablestack/rdo

Version:

A library to facilitate building and running graphs of Reactive Domain Objects - connecting JSON data sources to reactive client applications

253 lines 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RdoObjectNW = void 0; const __1 = require(".."); const __2 = require("../.."); const logger_1 = require("../../infrastructure/logger"); const types_1 = require("../../types"); const node_tracker_1 = require("../../infrastructure/node-tracker"); const logger = logger_1.Logger.make('RdoObjectNW'); class RdoObjectNW extends __1.RdoInternalNWBase { constructor({ value, typeInfo, key, mutableNodeCache, wrappedParentRdoNode, wrappedSourceNode, defaultEqualityComparer, syncChildNode, wrapRdoNode, globalNodeOptions, matchingNodeOptions, targetedOptionMatchersArray, eventEmitter, }) { super({ typeInfo, key, mutableNodeCache, wrappedParentRdoNode, wrappedSourceNode, syncChildNode, matchingNodeOptions, globalNodeOptions, targetedOptionMatchersArray, eventEmitter }); /** */ this.makeContinueSmartSyncFunction = ({ originalSourceNodePath }) => { // Build method const continueSmartSync = ({ sourceNodeSubPath = '', sourceNode, sourceNodeItemKey, rdoNode, rdoNodeItemKey }) => { const sourceNodeTypePath = sourceNodeSubPath ? `${originalSourceNodePath}${node_tracker_1.NodeTracker.nodePathSeperator}${sourceNodeSubPath}` : originalSourceNodePath; const sourceNodeInstancePath = `${sourceNodeTypePath}${node_tracker_1.NodeTracker.nodePathSeperator}${sourceNodeItemKey}`; const wrappedRdoNode = this._wrapRdoNode({ sourceNodeTypePath, sourceNodeInstancePath, sourceNode, sourceNodeItemKey: sourceNodeItemKey, rdoNode, rdoNodeItemKey: rdoNodeItemKey }); if (!types_1.isIRdoInternalNodeWrapper(wrappedRdoNode)) throw new Error(`(${sourceNodeTypePath}) makeContinueSmartSyncFunction can not be called on Leaf nodes`); return this.syncChildNode({ wrappedParentRdoNode: wrappedRdoNode, rdoNodeItemKey, sourceNodeItemKey }); }; // return method return continueSmartSync; }; this._value = value; this._equalityComparer = __2.IsICustomEqualityRDO(value) ? value.isStateEqual : defaultEqualityComparer; this._wrapRdoNode = wrapRdoNode; } //------------------------------ // Protected //------------------------------ /** */ getNodeInstanceCache() { let mutableNodeCacheItem = this.mutableNodeCache.get({ sourceNodeInstancePath: this.wrappedSourceNode.sourceNodeInstancePath }); if (!mutableNodeCacheItem) { mutableNodeCacheItem = { sourceData: null }; this.mutableNodeCache.set({ sourceNodeInstancePath: this.wrappedSourceNode.sourceNodeInstancePath, data: mutableNodeCacheItem }); } return mutableNodeCacheItem; } //------------------------------ // IRdoNodeWrapper //------------------------------ get isLeafNode() { return false; } get value() { return this._value; } childElementCount() { return 0; } smartSync() { let changed = false; const sourceNodeTypePath = this.wrappedSourceNode.sourceNodeTypePath; const rdo = this.value; const sourceObject = this.wrappedSourceNode.value; const previousSourceData = this.getNodeInstanceCache(); // Check if previous source state and new source state are equal const isAlreadyInSync = this._equalityComparer(sourceObject, previousSourceData.sourceData); // Call lifecycle methods if found if (__2.IsIBeforeSmartSync(rdo)) rdo.beforeSmartSync({ sourceObject, isSyncNeeded: !isAlreadyInSync }); if (!isAlreadyInSync) { // Call lifecycle methods if found if (__2.IsIBeforeSyncUpdate(rdo)) rdo.beforeSyncUpdate({ sourceObject }); // Synchronize if (__2.IsICustomSync(rdo)) { logger.debug(`'${sourceNodeTypePath}' synchronizeObjectState - needs sync - custom state synchronizer found. Using to sync`); changed = rdo.synchronizeState({ sourceObject, continueSmartSync: this.makeContinueSmartSyncFunction({ originalSourceNodePath: sourceNodeTypePath }) }); } else { logger.debug(`'${sourceNodeTypePath}' synchronizeObjectState - needs sync - no custom state synchronizer found. Using autoSync`, rdo); changed = this.sync(); logger.debug(`'${sourceNodeTypePath}' synchronizeObjectState - needs sync - post autoSync - changed: ${changed}`); } // Call lifecycle methods if found if (__2.IsIAfterSyncUpdate(rdo)) rdo.afterSyncUpdate({ sourceObject }); } else { logger.debug(`'${sourceNodeTypePath}' already in sync (synchronizeObjectState). Skipping`); } // Call lifecycle methods if found if (__2.IsIAfterSmartSync(rdo)) rdo.afterSyncIfNeeded({ sourceObject, rdoUpdateAttempted: !isAlreadyInSync, rdoWasChanged: changed }); // Update cache; previousSourceData.sourceData = sourceObject; return changed; } getSourceNodeKeys() { if (!types_1.isISourceObjectNodeWrapper(this.wrappedSourceNode)) throw new Error(`RDO object node can only be synced with Source object nodes (Path: '${this.wrappedSourceNode.sourceNodeTypePath}')`); return this.wrappedSourceNode.getNodeKeys(); } getSourceNodeItem(key) { if (!types_1.isISourceObjectNodeWrapper(this.wrappedSourceNode)) throw new Error(`RDO object node can only be synced with Source object nodes (Path: '${this.wrappedSourceNode.sourceNodeTypePath}')`); return this.wrappedSourceNode.getNodeItem(key); } //------------------------------ // IRdoInternalNodeWrapper //------------------------------ getRdoNodeItem(key) { return this._value[key]; } //-------------------------------------- // Private Methods //-------------------------------------- /** * */ sync() { var _a, _b; let changed = false; const wrappedSourceNode = this.wrappedSourceNode; if (!types_1.isISourceObjectNodeWrapper(this.wrappedSourceNode)) { throw new Error(`RDO object node can only be synced with Source object nodes (Path: '${this.wrappedSourceNode.sourceNodeTypePath}')`); } // Loop properties for (const sourceFieldname of wrappedSourceNode.getNodeKeys()) { const sourceFieldVal = wrappedSourceNode.getNodeItem(sourceFieldname); let rdoFieldname = this.getFieldname({ sourceFieldname: sourceFieldname, sourceFieldVal }); let rdoNodeItemValue; if (rdoFieldname) { rdoNodeItemValue = this.value[rdoFieldname]; } else { // Auto-create Rdo object field if autoMakeRdoTypes.objectFields // Note: this creates an observable tree in the exact shape of the source data // It is recommended to consistently use autoMakeRdo* OR consistently provide customMakeRdo methods. Blending both can lead to unexpected behavior // Keys made here, instantiation takes place in downstream constructors if ((_b = (_a = this.globalNodeOptions) === null || _a === void 0 ? void 0 : _a.autoMakeRdoTypes) === null || _b === void 0 ? void 0 : _b.objectFields) { logger.trace(`sourceNodeTypePath: ${this.wrappedSourceNode.sourceNodeTypePath} - domainFieldname '${sourceFieldname}' auto making RDO`, sourceFieldVal); // Allocate fieldname and empty val rdoFieldname = sourceFieldname; rdoNodeItemValue = this.makeRdoElement(sourceFieldVal); // Insert this.value[rdoFieldname] = rdoNodeItemValue; // Emit this.eventEmitter.publish('nodeChange', { changeType: 'add', sourceNodeTypePath: this.wrappedSourceNode.sourceNodeTypePath, sourceNodeInstancePath: this.wrappedSourceNode.sourceNodeInstancePath, sourceKey: sourceFieldname, rdoKey: rdoFieldname, previousSourceValue: undefined, newSourceValue: sourceFieldVal, }); } else { logger.trace(`sourceNodeTypePath: ${this.wrappedSourceNode.sourceNodeTypePath} - fieldname '${sourceFieldname}' key not found in RDO. Skipping property`); continue; } } // Update directly if Leaf node // Or else step into child and sync if (sourceFieldVal === null || sourceFieldVal === undefined || __1.NodeTypeUtils.isPrimitive(sourceFieldVal)) { logger.trace(`Skipping child sync and updating directly. Field '${rdoFieldname}' in object is Primitive, null, or undefined.`); changed = this.primitiveDirectSync({ sourceKey: sourceFieldname, rdoKey: rdoFieldname, previousValue: rdoNodeItemValue, newValue: sourceFieldVal }); } else { logger.trace(`Syncing Field '${rdoFieldname}' in object`); changed = this.syncChildNode({ wrappedParentRdoNode: this, rdoNodeItemKey: rdoFieldname, sourceNodeItemKey: sourceFieldname }) && changed; } } return changed; } /** * */ getFieldname({ sourceFieldname, sourceFieldVal }) { var _a, _b, _c; // Set Destination Prop Key, and if not found, fall back to name with prefix if supplied let rdoFieldname; // // Try IHasCustomRdoFieldNames // if (!rdoFieldname && __2.IsIHasCustomRdoFieldNames(this._value)) { rdoFieldname = this._value.tryGetRdoFieldname({ sourceNodeTypePath: this.wrappedSourceNode.sourceNodeTypePath, sourceFieldname, sourceFieldVal }); // If fieldName not in wrappedParentRdoNode, set to null if (rdoFieldname && !(rdoFieldname in this._value)) { rdoFieldname = undefined; } else { logger.trace(`rdoFieldname '${rdoFieldname}' found with IHasCustomRdoFieldNames`); } } // // Try _globalNodeOptions // if (!rdoFieldname && ((_a = this.globalNodeOptions) === null || _a === void 0 ? void 0 : _a.tryGetRdoFieldname)) { rdoFieldname = (_b = this.globalNodeOptions) === null || _b === void 0 ? void 0 : _b.tryGetRdoFieldname({ sourceNodeTypePath: this.wrappedSourceNode.sourceNodeTypePath, sourceFieldname, sourceFieldVal }); // If fieldName not in wrappedParentRdoNode, set to null if (rdoFieldname && !(rdoFieldname in this._value)) { rdoFieldname = undefined; } else { logger.trace(`rdoFieldname '${rdoFieldname}' found with _globalNodeOptions.tryGetRdoFieldname`); } } // // Try straight match for sourceFieldname if (!rdoFieldname) { rdoFieldname = sourceFieldname; if (rdoFieldname && !(rdoFieldname in this._value)) { rdoFieldname = undefined; } else { logger.trace(`rdoFieldname '${rdoFieldname}' found - straight match for sourceFieldname`); } } // // Try commonRdoFieldnamePostfix // if (!rdoFieldname && ((_c = this.globalNodeOptions) === null || _c === void 0 ? void 0 : _c.commonRdoFieldnamePostfix)) { const domainPropKeyWithPostfix = `${sourceFieldname}${this.globalNodeOptions.commonRdoFieldnamePostfix}`; rdoFieldname = domainPropKeyWithPostfix; // If fieldName not in wrappedParentRdoNode, set to null if (rdoFieldname && !(rdoFieldname in this._value)) { rdoFieldname = undefined; } else { logger.trace(`rdoFieldname '${rdoFieldname}' found with commonRdoFieldnamePostfix`); } } return rdoFieldname; } /** */ primitiveDirectSync({ sourceKey, rdoKey, previousValue, newValue }) { if (Object.is(previousValue, newValue)) { logger.trace(`smartSync - SourceNodePath:${this.wrappedSourceNode.sourceNodeTypePath}, values evaluate to Object.is equal. Not allocating value`, newValue); return false; } logger.trace(`primitive value found in domainPropKey ${rdoKey}. Setting from old value to new value`, previousValue, newValue); this.value[rdoKey] = newValue; this.eventEmitter.publish('nodeChange', { changeType: 'update', sourceNodeTypePath: this.wrappedSourceNode.sourceNodeTypePath, sourceNodeInstancePath: this.wrappedSourceNode.sourceNodeInstancePath, sourceKey, rdoKey, previousSourceValue: previousValue, newSourceValue: newValue, }); return true; } } exports.RdoObjectNW = RdoObjectNW; //# sourceMappingURL=rdo-object-nw.js.map