diff --git a/core/event/change-event.js b/core/event/change-event.js index 194df6afc..a51c1e329 100644 --- a/core/event/change-event.js +++ b/core/event/change-event.js @@ -177,6 +177,10 @@ var ChangeEvent = exports.ChangeEvent = Montage.specialize({ */ removedValues: { value: undefined + }, + + tracksDispatchChain: { + value: true } }); diff --git a/core/event/event-manager.js b/core/event/event-manager.js index 5aacf9ddf..20f52d15c 100644 --- a/core/event/event-manager.js +++ b/core/event/event-manager.js @@ -3196,6 +3196,7 @@ var EventManager = exports.EventManager = Montage.specialize(/** @lends EventMan } promise = this._invokeTargetListenerEntryForEvent(iTarget, nextEntry, mutableEvent, mutableEventPhase, undefined/*currentTargetIdentifierSpecificCaptureMethodName*/, undefined/*identifierSpecificCaptureMethodName*/, undefined/*captureMethodName*/, previousPromise); + // if(previousPromise && promise) { // if(!promises) { // promises = [previousPromise, promise]; @@ -3238,11 +3239,85 @@ var EventManager = exports.EventManager = Montage.specialize(/** @lends EventMan } }, + _activeEventsByType: { + get: function () { + return this.__activeEventsByType || (this.__activeEventsByType = new Map()); + } + }, + + _eventQueueByType: { + get: function () { + return this.__eventQueueByType || (this.__eventQueueByType = new Map()); + } + }, + + _trackedTargets: { + value: new Set(["Person", "EmploymentPositionStaffing", "EmploymentPosition", "JobRole"]) + }, + + handleEvent: { + enumerable: false, + value: function (event) { + + if (typeof window === "object" && !window._eventQueueByType) { + window.eventQueueByType = this._eventQueueByType; + window.activeEventsByType = this._activeEventsByType; + } + if (typeof window === "undefined") { + this._handleEvent(event); + return; + } + + if (event.target.name && this._trackedTargets.has(event.target.name) && event.type.indexOf("Operation") !== -1) { + let queued = this._activeEventsByType.has(event.type) && !this._hasActiveReferrer(event); + console.log("EventManager Event", event.target.name, event.id, event.referrerId, event, { + queued: queued, + firstOfKind: !queued && !this._activeEventsByType.has(event.type) + }); + } + if (this._activeEventsByType.has(event.type) && !this._hasActiveReferrer(event)) { + if (!this._eventQueueByType.has(event.type)) { + this._eventQueueByType.set(event.type, []); + } + this._eventQueueByType.get(event.type).unshift(event); + } else if (!this._activeEventsByType.has(event.type)) { + this._activeEventsByType.set(event.type, new Set([event])); + this._handleEvent(event); + } else { + this._activeEventsByType.get(event.type).add(event); + this._handleEvent(event); + } + } + }, + + _hasActiveReferrer: { + value: function (event) { + let referrer; + if (event.referrer) { + return this._isActive(event.referrer) || this._hasActiveReferrer(event.referrer); + } else if (event.referrers) { + let i, n, isActive; + for (i = 0, n = event.referrers.length; i < n && !isActive; ++i) { + isActive = this._isActive(event.referrers[i]) || this._hasActiveReferrer(event.referrers[i]); + } + return isActive; + } + return false; + } + }, + + _isActive: { + value: function (event) { + let activeByType = this._activeEventsByType.get(event.type); + return !!activeByType && activeByType.has(event); + } + }, + /** @function @param {Event} event The handled event. */ - handleEvent: { + _handleEvent: { enumerable: false, value: function EventManager_handleEvent(event) { // if(event.type === "pointerdown" || event.type === "pointerup" || event.type.indexOf("press") !== -1) { @@ -3264,6 +3339,24 @@ var EventManager = exports.EventManager = Montage.specialize(/** @lends EventMan targetEntry, targetEntryForEventType, promise; + if (event.target.name && this._trackedTargets.has(event.target.name) && event.type.indexOf("Operation") !== -1) { + console.log("EventManager HandleEvent", event.target.name, event.id, event.referrerId); + } + + // if (event.tracksDispatchChain) { + // if (this._previousEvent && this._previousEvent.dispatchChain && this._previousEvent.dispatchChain.length && event.dispatchChain) { + // // console.log(`[EventManager] Mark Event ${event.target.objectDescriptor.name}`); + // event.dispatchChain.push.apply(event.dispatchChain, this._previousEvent.dispatchChain); + // event.dispatchChain.push(this._previousEvent); + // } else if (this._previousEvent && event.dispatchChain) { + // // console.log(`[EventManager] Push Previous Event ${event.target.objectDescriptor.name}`); + // event.dispatchChain.push(this._previousEvent); + // } else { + // // console.log(`[EventManager] Record initial event ${event.target.objectDescriptor.name}`); + // } + // this._previousEvent = event; + // } + if(this.isBrowser) { if( (MontageElement && event.target instanceof MontageElement) || @@ -3368,7 +3461,25 @@ var EventManager = exports.EventManager = Montage.specialize(/** @lends EventMan if(promise) { var self = this; + let timeoutId = setTimeout(function () { + if (mutableEvent.target && mutableEvent.target.name) { + if (mutableEvent.referredOperations && mutableEvent.referredOperations.length) { + let ancestry = mutableEvent.referredOperations.map(function (referred) { + return " " + referred.type + ":" + referred.target.name + ":" + referred.id + ":" + referred.rawDataService?.name; + }).join("\n"); + ancestry = "Referred: \n" + ancestry; + console.log("Unresolved Event", mutableEvent.type, mutableEvent.target.name, mutableEvent.id, ancestry, mutableEvent); + + } else { + console.log("Unresolved Event", mutableEvent.type, mutableEvent.target.name, mutableEvent.id, mutableEvent); + } + } else { + console.log("Unresolved Event", mutableEvent.type, mutableEvent.id, mutableEvent); + } + + }, 2000); mutableEvent.propagationPromise = promise.then(function() { + clearTimeout(timeoutId); self._finalizeHandleEvent(mutableEvent, event); }); } else { @@ -3398,9 +3509,39 @@ var EventManager = exports.EventManager = Montage.specialize(/** @lends EventMan document.body.removeEventListener("DOMAttrModified", this.domModificationEventHandler, true); document.body.removeEventListener("DOMCharacterDataModified", this.domModificationEventHandler, true); } + + // if (mutableEvent.target && mutableEvent.target.name && mutableEvent.type.indexOf("Operation") !== -1) { + // console.log("EventManager _finalizeHandleEvent", mutableEvent.type, mutableEvent.target.name, mutableEvent.id, mutableEvent.referrerId, mutableEvent); + // } + + //Handle next event + let type = mutableEvent.type, + next; + if (this._eventQueueByType.has(type) && this._eventQueueByType.get(type).length) { + next = this._eventQueueByType.get(type).pop(); + this._activeEventsByType.get(type).delete(event); + this._activeEventsByType.get(type).add(next); + this._handleEvent(next); + } else { + this._activeEventsByType.delete(type); + this._eventQueueByType.delete(type); + } } + + // if (this._previousEvent === event || this._previousEvent === mutableEvent) { + // this._previousEvent = event.dispatchChain[event.dispatchChain.length - 1]; + // let name = event.target.name || event.target.objectDescriptor.name, + // previousName; + // if (this._previousEvent) { + // previousName = this._previousEvent.target.name || this._previousEvent.target.objectDescriptor.name; + // // console.log(`[EventManager] Reset previous event ${event.identifier} ${name} ${event.type} to ${this._previousEvent.identifier} ${previousName} ${this._previousEvent.type}`); + // } else { + // // console.log(`[EventManager] Clear previous event ${event.identifier} ${name} ${event.type}`); + // } + // } + } }, @@ -3572,6 +3713,11 @@ var EventManager = exports.EventManager = Montage.specialize(/** @lends EventMan this.unregisterTargetEventListener(iTarget, mutableEvent.type, listener, listenerEntry); } + if (result && result.then) { + mutableEvent.triggeredTargets = mutableEvent.triggeredTargets || []; + mutableEvent.triggeredTargets.push({target: iTarget, entry: listenerEntry, name: currentTargetIdentifierSpecificPhaseMethodName, result: result}); + } + return result; } diff --git a/core/event/mutable-event.js b/core/event/mutable-event.js index 0e414b1ac..b379f3ed9 100644 --- a/core/event/mutable-event.js +++ b/core/event/mutable-event.js @@ -394,12 +394,37 @@ var wrapPropertyGetter = function (key, storageKey) { }, /** * @type {Property} - * @default {Element} null + * @default {Array} null */ composedPath: { value: function () { return this._composedPath; } + }, + + /** + * @type {Property} + * @default {Array} null + * An ordered array containing the chain of events that led to the + * dispatch of this one + */ + dispatchChain: { + get: function () { + return this.tracksDispatchChain ? this._dispatchChain || (this._dispatchChain = []) : null; + } + }, + + /** + * @type {Property} + * @default {Boolean} false + * Indicates whether the ancestory if this event will be tracked. It is expensive + * so this is only set to true for clearly defined use cases + */ + tracksDispatchChain: { + // value: false + get: function () { + return (this._tracksDispatchChain !== void 0) ? this._tracksDispatchChain : (this._event && this._event.tracksDispatchChain); + } } }, { diff --git a/data/converter/raw-foreign-value-to-object-converter.js b/data/converter/raw-foreign-value-to-object-converter.js index 6ab19dc73..d67747a59 100644 --- a/data/converter/raw-foreign-value-to-object-converter.js +++ b/data/converter/raw-foreign-value-to-object-converter.js @@ -1,10 +1,12 @@ var RawValueToObjectConverter = require("./raw-value-to-object-converter").RawValueToObjectConverter, Criteria = require("../../core/criteria").Criteria, + DataOperation = require("../service/data-operation").DataOperation, DataQuery = require("../model/data-query").DataQuery, Map = require("../../core/collections/map").Map, syntaxProperties = require("../../core/frb/syntax-properties"), Uuid = require("core/uuid").Uuid, Promise = require("../../core/promise").Promise; + /** * @class RawForeignValueToObjectConverter * @classdesc Converts a property value of raw data to the referenced object. @@ -122,7 +124,7 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( } }, _fetchConvertedDataForObjectDescriptorCriteria: { - value: function(typeToFetch, criteria, currentRule, registerMappedPropertiesAsChanged) { + value: function(typeToFetch, criteria, currentRule, registerMappedPropertiesAsChanged, referrerOperation) { var self = this; return this.service ? this.service.then(function (service) { @@ -131,6 +133,10 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( //var localResult, localPartialResult; + if (typeToFetch.name === "EmploymentPosition" || typeToFetch.name === "EmploymentPositionStaffing" || typeToFetch.name === "Organization" ) { + console.log("ExpressionDataMapping._fetchConvertedDataForObjectDescriptorCriteria", typeToFetch, currentRule.propertyDescriptor.name, referrerOperation && referrerOperation.id, criteria); + } + /* Leaving a trace of localPartialResult here. Unless I'm missing something, the problem with a partial result is that we don't really have an easy way to return a partial result with the promise-based API, @@ -208,6 +214,10 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( query.hints = {rawDataService: service}; + if (referrerOperation) { + query.hints.referrerOperation = referrerOperation; + } + if(registerMappedPropertiesAsChanged){ query.hints.registerMappedPropertiesAsChanged = registerMappedPropertiesAsChanged; } @@ -351,12 +361,16 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( if(!queryParts) { queryParts = { criteria: [], - readExpressions: []/*, + readExpressions: [], + referrerOperations: []/*, hints: {}*/ }; self._pendingCriteriaByTypeToCombine.set(typeToFetch, queryParts); } queryParts.criteria.push(criteria); + if (referrerOperation) { + queryParts.referrerOperations.push(referrerOperation); + } /* Sounds twisted, but this is to deal with the case where we need to fetch to resolve a property of the object itself. @@ -495,6 +509,10 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( query.hints.registerMappedPropertiesAsChanged = registerMappedPropertiesAsChanged; } + if(queryParts.referrerOperations) { + query.hints.referrerOperations = queryParts.referrerOperations; + } + //console.log("_combineFetchDataMicrotaskFunctionForTypeQueryParts query:",query); mapIterationFetchPromise = rootService.fetchData(query) @@ -695,9 +713,23 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( value: Uuid.noValue }, + _findScopedDataOperation: { + value: function (scope) { + let operation; + while (scope.parent && !operation) { + if (scope.value instanceof DataOperation) { + operation = scope.value; + } + scope = scope.parent; + } + return operation; + } + }, + _convert: { value: function (scope, service) { - var v = scope.value; + var v = scope.value, + referrerOperation = this._findScopedDataOperation(scope); if((v && !(v instanceof Array )) || (v instanceof Array && v.length > 0)) { var self = this, //We put it in a local variable so we have the right value in the closure @@ -706,6 +738,8 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( criteria, query; + + if(this.foreignDescriptorMappings) { /* Needs to loop on the mapping and evaluate value. If v is an array, it's possible @@ -736,7 +770,7 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( aCriteria; while (anObjectDescriptor = mapIterator.next().value) { aCriteria = this.convertCriteriaForValue(groupMap.get(anObjectDescriptor)); - promises.push(this._fetchConvertedDataForObjectDescriptorCriteria(anObjectDescriptor, aCriteria, currentRule, registerMappedPropertiesAsChanged)); + promises.push(this._fetchConvertedDataForObjectDescriptorCriteria(anObjectDescriptor, aCriteria, currentRule, registerMappedPropertiesAsChanged, referrerOperation)); } @@ -763,7 +797,7 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( foreignKeyValue = v[rawDataProperty], aCriteria = this.convertCriteriaForValue(foreignKeyValue); - return this._fetchConvertedDataForObjectDescriptorCriteria(valueDescriptor, aCriteria, currentRule, registerMappedPropertiesAsChanged); + return this._fetchConvertedDataForObjectDescriptorCriteria(valueDescriptor, aCriteria, currentRule, registerMappedPropertiesAsChanged, referrerOperation); } else { return Promise.resolve(null); @@ -783,7 +817,7 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( return this._descriptorToFetch.then(function (typeToFetch) { - return self._fetchConvertedDataForObjectDescriptorCriteria(typeToFetch, criteria, currentRule, registerMappedPropertiesAsChanged); + return self._fetchConvertedDataForObjectDescriptorCriteria(typeToFetch, criteria, currentRule, registerMappedPropertiesAsChanged, referrerOperation); // if (self.serviceIdentifier) { // criteria.parameters.serviceIdentifier = self.serviceIdentifier; diff --git a/data/model/data-event.js b/data/model/data-event.js index 27b62c055..f96bb5124 100644 --- a/data/model/data-event.js +++ b/data/model/data-event.js @@ -58,6 +58,10 @@ exports.DataEvent = MutableEvent.specialize({ detail: { value: undefined + }, + + tracksDispatchChain: { + value: true } @@ -101,4 +105,5 @@ exports.DataEvent = MutableEvent.specialize({ value: "saveChangesProgress" } + }); diff --git a/data/service/data-operation.js b/data/service/data-operation.js index 7fc214580..502201a8c 100644 --- a/data/service/data-operation.js +++ b/data/service/data-operation.js @@ -270,6 +270,7 @@ exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithM serializer.setProperty("timeStamp", this.timeStamp); serializer.setProperty("clientId", this.clientId); + if(this.target) { serializer.setProperty("target", this.target); @@ -297,10 +298,14 @@ exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithM serializer.setProperty("referrerId", this.referrerId); } - if(this.referrer) { + if (this.referrer) { serializer.setProperty("referrer", this.referrer); } + if (this.referrers) { + serializer.setProperty("referrers", this.referrers); + } + serializer.setProperty("criteria", this._criteria); /* @@ -420,6 +425,10 @@ exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithM this.referrer = value; } + value = deserializer.getProperty("referrers"); + if (value !== void 0) { + this.referrers = value; + } value = deserializer.getProperty("criteria"); if (value !== void 0) { @@ -655,6 +664,25 @@ exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithM value: undefined }, + + + /************ + * Design Discussion 6/9/2026 + * + * 'referrer' and 'referrers' are needed in distinct use cases. We should make this explicit in + * one of two ways. + * 1. A class to encapsulate referrers e.g. OperationReferrer that can encapsulate 1 or + * more referrers. DataOperation.referrer would be an instance of this class + * + * 2. Subclasses of DataOperation that separate the use cases. One subclass would have referrer. + * The other would have referrers. + * Examples: + * - InitiatingReadOperation or RootReadOperation - A ReadOperation for a query sent from the app-level. No referrer property needed. + * - MergedReadOperation or NestedReadOperation - A ReadOperation for multiple merged queries. 'referrers' property + * - ReadCompletionOperation - A completion operation can only be associated to a single read. 'referrer' property + * + */ + /** * An operation that preceded and this one is related to. For a ReadUpdated, it would be the Read operation. * @@ -665,32 +693,64 @@ exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithM }, referrer: { get: function() { - return this._referrer || ( - this._referrer === null - ? null - : (this._referrer = this?.rawDataService?.referrerForDataOperation(this)) - ); + if (this._referrer || this._referrer === null) { + return this._referrer; + } + + this._referrer = this?.rawDataService?.referrerForDataOperation(this); + if (this._referrer) { + this._referrer.referredOperations.push(this); + } + return this._referrer; }, set: function(value) { + if (Array.isArray(value)) { + debugger; + } if(value !== this._referrer) { - this.referreredOperations.delete(this._referrer); + if (this._referrer) { + this._referrer.referredOperations.delete(this); + } this._referrer = value; - this.referreredOperations.push(this._referrer); + if (this._referrer) { + if (this._referrer.referredOperations) { + this._referrer.referredOperations.push(this); + } else { + debugger; + this._referrer.referredOperations = [this]; + } + + } } } }, + referrers: { + get: function () { + return this._referrers; + }, + set: function (value) { + this._referrers = value; + if (Array.isArray(value)) { + value.forEach((referrer) => { + referrer.referredOperations.push(this); + }); + } + } + }, + + /** * inverse of referrer, an array of operations that has this as their referrer. * * @type {Array} */ - _referreredOperations: { + _referredOperations: { value: undefined }, - referreredOperations: { + referredOperations: { get: function() { - return this._referreredOperations || (this._referreredOperations = []); + return this._referredOperations || (this._referredOperations = []); } }, @@ -1093,6 +1153,10 @@ exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithM } return this._objectDescriptors; } + }, + + tracksDispatchChain: { + value: true } diff --git a/data/service/data-service.js b/data/service/data-service.js index 210ffbf3a..cd7b3fbb1 100644 --- a/data/service/data-service.js +++ b/data/service/data-service.js @@ -6486,6 +6486,13 @@ DataService.addClassProperties( // make sure type is an object descriptor or a data object descriptor. query.type = this.objectDescriptorForType(query.type); + // if (query.type.name === "EmploymentPosition" || query.type.name === "EmploymentPositionStaffing" || query.type.name === "Organization") { + // let referred = query.hints.referrerOperations ? query.hints.referrerOperations.map(function (operation) { + // return operation.id + // }).join(",") : query.hints.referrerOperation ? query.hints.referrerOperation.id : ""; + // console.log("DataService.fetchData", query.type.name, referred); + // } + if (this.supportsDataOperation) { //Check if we already have a DataStream pending for that same query: if ((stream = this.registeredDataStreamMapForDataQuery(query))) { @@ -6552,6 +6559,8 @@ DataService.addClassProperties( // stream.dataError(e); // } + + try { var readOperation = new DataOperation(); @@ -6589,7 +6598,11 @@ DataService.addClassProperties( if (query.criteria) { //readOperation.criteria = criteria.clone(); readOperation.criteria = query.criteria; + if (query.criteria.parameters === "019a4e83-203a-76de-bf6f-7f26739d65fd") { + window.suborgOperation = readOperation; + } } + if (query.sizeLimit) { readOperation.data.sizeLimit = query.sizeLimit; } @@ -6620,7 +6633,14 @@ DataService.addClassProperties( if (query.hints) { readOperation.hints = query.hints; + if (query.hints.referrerOperation) { + readOperation.referrer = query.hints.referrerOperation; + } + if (query.hints.referrerOperations) { + readOperation.referrers = query.hints.referrerOperations; + } } + /* this is half-assed, we're mapping full objects to RawData, but not the properties in the expression. @@ -7228,6 +7248,7 @@ DataService.addClassProperties( - The snapshot may not help us since it could be a property resolved off the primary key if(trigger._getValueStatus(object) == ) + */ //Commentting out the restriction to exclude created objects as we might want to //use it for them as well @@ -7235,11 +7256,32 @@ DataService.addClassProperties( // if(!this._createdDataObjects || (this._createdDataObjects && !this._createdDataObjects.has(changeEvent.target))) { //Needs to register the change so saving changes / update operations can use it later to decise what to send //console.log("handleChange:",changeEvent); + if (changeEvent.dispatchChain.length > 0) { + // debugger; + // this._logEventChain(changeEvent); + } this.registerDataObjectChangesFromEvent(changeEvent); //} }, }, + _logEventChain: { + value: function (changeEvent) { + let name = changeEvent.target.name || changeEvent.target.objectDescriptor.name, + message = [ + `Change Event Tracking`, + `${changeEvent.identifier} ${name} ${changeEvent.type} --> ` + ]; + + changeEvent.dispatchChain.forEach((event) => { + message.push(`${event.identifier} ${event.target.name || event.target.objectDescriptor.name} ${event.type} --> `); + }); + + console.log(message.join("\n")); + + } + }, + /*************************************************************************** * Saving Data */ diff --git a/data/service/expression-data-mapping.js b/data/service/expression-data-mapping.js index 300f21b61..3323fd8ee 100644 --- a/data/service/expression-data-mapping.js +++ b/data/service/expression-data-mapping.js @@ -1396,6 +1396,10 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData */ service.mappingWillMapRawDataToObjectProperty(this, data, object, aRule.targetPath, context, mappingScope); + if (data.employeeId === "8b286280-6944-432f-8940-39be0117b14f" && object.dataIdentifier.typeName === "EmploymentPositionStaffing") { + console.log("ExpressionDataMapping.mapProperty START", aRule.targetPath); + } + // console.log("mapRawDataToObject "+ this.service.dataIdentifierForObject(object)+" WILL MAP "+ aRule.targetPath); result = this.mapRawDataToObjectProperty(data, object, aRule.targetPath, context, mappingScope, registerMappedPropertiesAsChanged); @@ -1403,7 +1407,22 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData if (this._isAsync(result)) { const targetPath = aRule.targetPath, propertyDescriptor = aRule.propertyDescriptor; + if ((data.employeeId === "8b286280-6944-432f-8940-39be0117b14f" || object.dataIdentifier.typeName === "EmploymentPositionStaffing") + || (data.id === "8b286280-6944-432f-8940-39be0117b14f" || object.dataIdentifier.typeName === "EmploymentPosition") || + (object.dataIdentifier.typeName === "JobRole")) { + // console.log("ExpressionDataMapping.mapProperty START", targetPath); + window.unmappedProperties = window.unmappedProperties || new Set(); + window.unmappedProperties.add(object.dataIdentifier.typeName + "." + targetPath); + } + result = result.then((resultValue) => { + if ((data.employeeId === "8b286280-6944-432f-8940-39be0117b14f" || object.dataIdentifier.typeName === "EmploymentPositionStaffing") + || (data.id === "8b286280-6944-432f-8940-39be0117b14f" || object.dataIdentifier.typeName === "EmploymentPosition") || + (object.dataIdentifier.typeName === "JobRole")) { + // console.log("ExpressionDataMapping.mapProperty DONE", targetPath); + window.unmappedProperties.delete(object.dataIdentifier.typeName + "." + targetPath); + } + // console.log("ExpressionDataMapping.mapProperty DONE", targetPath); this._registerMappedPropertyValueAsChangesForCreatedObject(targetPath, resultValue, (changesForDataObject || (changesForDataObject = service.changesForDataObject(object))), object, (mainService || (mainService = service.mainService))); /* Tell our service: mappingDidMapRawDataToObjectPropertyValue @@ -1414,6 +1433,9 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData return resultValue; }); } else { + if (data.employeeId === "8b286280-6944-432f-8940-39be0117b14f" && object.dataIdentifier.typeName === "EmploymentPositionStaffing") { + console.log("ExpressionDataMapping.mapProperty DONE", aRule.targetPath); + } this._registerMappedPropertyValueAsChangesForCreatedObject(aRule.targetPath, result, (changesForDataObject || (changesForDataObject = service.changesForDataObject(object))), object, (mainService || (mainService = service.mainService))); /* Tell our service: mappingDidMapRawDataToObjectPropertyValue @@ -1657,6 +1679,24 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData * */ mapRawDataToObjectProperty: { + value: function (data, object, propertyName, context, scope, registerMappedPropertiesAsChanged) { + + if (object.objectDescriptor.name === "Person" || object.dataIdentifier.typeName === "EmploymentPositionStaffing" || object.dataIdentifier.typeName === "EmploymentPosition") { + let timeoutid = setTimeout(function () { + console.log("ExpressionDataMapping: Failed to map " + propertyName + " on " + object.objectDescriptor.name, data, context, scope); + }, 4000); + let result = this.___mapRawDataToObjectProperty(data, object, propertyName, context, scope, registerMappedPropertiesAsChanged); + result.then(function () { + clearTimeout(timeoutid); + }); + return result; + } else { + return this.___mapRawDataToObjectProperty(data, object, propertyName, context, scope, registerMappedPropertiesAsChanged); + } + } + }, + + ___mapRawDataToObjectProperty: { value: function (data, object, propertyName, context, scope = this._scope.nest(data), registerMappedPropertiesAsChanged) { var rule = this.objectMappingRuleForPropertyName(propertyName), propertyDescriptor = rule && this.objectDescriptor.propertyDescriptorForName(propertyName); @@ -1670,6 +1710,10 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData locales, debug = DataService.debugProperties.has(propertyName) || (rule && rule.debug === true); + if (object.objectDescriptor.name === "Person" && (propertyName === "employmentPosition" || propertyName === "employmentHistory")) { + console.log("ExpressionDataMapping 2: " + object.objectDescriptor.name + " " + propertyName, context, data); + } + //Simplistic and potentially wrong, but if there's no properties on data for that rule, then there's no point doing it // if(rule && !data.hasOwnProperty(rule.sourcePath)) { diff --git a/data/service/http-service.js b/data/service/http-service.js index b683a3bc4..87bd165db 100644 --- a/data/service/http-service.js +++ b/data/service/http-service.js @@ -113,6 +113,10 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService console.debug(this.name+" handleReadOperation(): EmploymentPosition read operation "+readOperation.id+" for "+readOperation.data.readExpressions+", "+readOperation.criteria.toString()); } + // if(readOperation.target.name === "Person" || ) { + console.log(this.name+" handleReadOperation(): read operation "+readOperation.id); + // } + /* This is to temporarily prevents a RawDataService to attempt to handle a readOperation that comes from a query coming from a mapping's converter that will have a very RawData-specific criteria that @@ -161,6 +165,14 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService } } + + let timeoutId = setTimeout(function () { + console.log("HttpService Unresolved promise", readOperation.target.name, readOperation.id, readOperation); + }, 2000); + readOperationCompletionPromise.then(function () { + clearTimeout(timeoutId); + }) + //If we've been asked to return a promise for the read Completion Operation, we do so. Again, this is fragile. IT HAS TO MOVE UP TO RAW DATA SERVICE //WE CAN'T RELY ON INDIVIDUAL DATA SERVICE IMPLEMENTORS TO KNOW ABOUT THAT... if (this.promisesReadCompletionOperation) { @@ -198,6 +210,11 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService readOperationCompletionPromise = readOperationCompletionPromiseResolve = readOperationCompletionPromiseReject = undefined; } + let isTracking = readOperation.target.name === "Person" || readOperation.target.name === "EmploymentPositionStaffing"; + // if(readOperation.target.name === "Person") { + // console.log(this.name+" handleReadOperation(): Person read operation "+readOperation.id); + // } + if (this.authenticationPolicy && this.authenticationPolicy === AuthenticationPolicy.UP_FRONT) { let identityPromise; @@ -290,6 +307,10 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService return resolvedIdentity.objectDescriptor.propertyDescriptorNamed("accessTokens").valueDescriptor .then((resolvedAccessTokenDescriptor) => { + if (isTracking) { + console.log("HttpService Resolve AccessTokenDescriptor", readOperation.id); + } + let accessTokenDescriptor = resolvedAccessTokenDescriptor ? resolvedAccessTokenDescriptor : this.accessTokenDescriptor; if (!registeredAccessToken && !accessTokenDescriptor) { @@ -298,6 +319,10 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService } else { + if (isTracking) { + console.log("HttpService Request AccessToken", readOperation.id); + } + /* In the case where the identity is cached locally, and the access token is too. @@ -310,7 +335,12 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService tokenDataQuery.identity = resolvedIdentity; tokenQueryDataStream = this.mainService.fetchData(tokenDataQuery); + tokenDataQuery.hints = {referrerOperation: readOperation}; + return tokenQueryDataStream.then((result) => { + if (isTracking) { + console.log("HttpService Resolve AccessToken", readOperation.id); + } if (result && result.length === 1) { let accessToken = result[0]; this.registerAccessTokenForIdentity(accessToken, resolvedIdentity); @@ -330,7 +360,10 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService }) .catch(error => { - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, error, null); + if (isTracking) { + console.log("HttpService Failed AccessToken", readOperation.id); + } + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), error, null); console.error("Identity promise failed with error", error); return responseOperation; }); @@ -359,8 +392,11 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService this.mapDataOperationToRawDataOperations(readOperation, readOperations) .then(() => { + if (isTracking) { + console.log("HttpService MapToRawOperations AccessToken", readOperation.id, readOperations?.length === 0); + } if(readOperations?.length === 0) { - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, null, []); + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), null, []); responseOperation.target.dispatchEvent(responseOperation); @@ -382,6 +418,10 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService } else { + if (isTracking) { + console.log("HttpService MapToRequests", readOperation.id, readOperations?.length === 0); + } + let iMapping = this.mappingForObjectDescriptor(iReadOperation.target); if (typeof iMapping.mapDataOperationToFetchRequests === "function") { iMapping.mapDataOperationToFetchRequests(iReadOperation, fetchRequests); @@ -427,8 +467,8 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService // rawDataObject[iReadExpression] = iPropertyDescriptor.defaultValue || iPropertyDescriptor.defaultFalsyValue; // } - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, null, rawData); - console.log("\t" + this.identifier + " handleReadOperation dispatch A responseOperation " + responseOperation.id, " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), null, rawData); + console.log("\t" + this.identifier + " handleReadOperation dispatch A responseOperation " + responseOperation.id, responseOperation.referrer.id, " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); responseOperation.target.dispatchEvent(responseOperation); @@ -444,10 +484,10 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService if (readOperation.clientId) { error.stack = null; } - // responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, error, null); + // responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), error, null); //Send an empty response instead - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, null, []); - console.log("\t" + this.identifier + " handleReadOperation dispatch B responseOperation " + responseOperation.id, " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), null, []); + console.log("\t" + this.identifier + " handleReadOperation dispatch B responseOperation " + responseOperation.id, responseOperation.referrer.id, " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); responseOperation.target.dispatchEvent(responseOperation); @@ -493,7 +533,7 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService }) .catch((error) => { - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, error, null); + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), error, null); console.error(error); console.log("\t" + this.identifier + " handleReadOperation ERROR dispatch D responseOperation " + responseOperation.id, " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); responseOperation.target.dispatchEvent(responseOperation); @@ -770,7 +810,7 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService mapping.mapFetchResponseToRawData(responseContent, rawData); //console.debug("rawData: ",rawData); - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, null, rawData, false /*isNotLast*/, readOperation.target/*responseOperationTarget*/); + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), null, rawData, false /*isNotLast*/, readOperation.target/*responseOperationTarget*/); console.log("\t" + this.identifier + " handleReadOperation dispatch " + responseOperation.type + " id " + responseOperation.id + " for read referrer id " + responseOperation.referrer.id + " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); responseOperation.target.dispatchEvent(responseOperation); @@ -784,7 +824,7 @@ var HttpService = exports.HttpService = class HttpService extends RawDataService }) .catch((error) => { console.log(this.name + " Fetch Request:", iRequest + ", error: ", error); - let responseOperation = this.responseOperationForReadOperation(readOperation.referrer ? readOperation.referrer : readOperation, error, null); + let responseOperation = this.responseOperationForReadOperation(this.relevantOperationForResponse(readOperation), error, null); console.error(error); console.log(this.identifier + " handleReadOperation dispatch ERROR responseOperation " + responseOperation.id, " for " + responseOperation.referrer.target.name + " like " + responseOperation.referrer.criteria); diff --git a/data/service/mux/synchronization-data-service.js b/data/service/mux/synchronization-data-service.js index f79730964..6b0e25648 100644 --- a/data/service/mux/synchronization-data-service.js +++ b/data/service/mux/synchronization-data-service.js @@ -123,13 +123,14 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu get _trackedTypes() { if (!this.__trackedTypes) { this.__trackedTypes = new Set([ - "JobRole" + "Person", "EmploymentPosition", "EmploymentPositionStaffing", "Organization", "IncorporatedOrganization", "JobRole" ]); } return this.__trackedTypes; } _logTypeEvent(objectDescriptor, /*message, element1, element2, ... elementN*/) { + let stack = new Error().stack; let args = Array.from(arguments), logArgs = args.slice(1); if (typeof logArgs[0] === "string") { @@ -137,6 +138,7 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu } else { logArgs.unshift("[SynchronizationDataService]"); } + // logArgs.push(stack.split("\n").slice(1, 4).join("\n")); if (!this._trackedTypes || !this._trackedTypes.size) { console.log.apply(console, logArgs); } else if (this._trackedTypes.has(objectDescriptor.name)) { @@ -751,6 +753,9 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu // mappedObjects.push(dataObject); + // if (objectDescriptor.name === "Person" && rawData.id === "8b286280-6944-432f-8940-39be0117b14f") { + // debugger; + // } /* We're syncing, so we need to grab as much data as we can if we have no guidelines @@ -1141,7 +1146,7 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu // iObject.originDataSnapshot = rawData[i]; if( rawData[i]) { - this._logTypeEvent(readCompletedOperation.referrer.target, "<<<<<<<<<<<<<<<<<<<<<<<<<<<-> Service capture ReadCompletedOperation "+readCompletedOperation.id+": _syncObjectDescriptorRawDataFromReadCompletedOperation from "+readCompletedOperation.rawDataService.identifier+", referrer "+readCompletedOperation.referrer.id+", for "+readCompletedOperation.referrer.target.name + (readCompletedOperation.referrer?.data?.readExpressions? (" "+readCompletedOperation.referrer?.data?.readExpressions) : "") + " like "+ readCompletedOperation.referrer.criteria); + this._logTypeEvent(readCompletedOperation.referrer.target, "<<<<<<<<<<<<<<<<<<<<<<<<<<<-> Service capture ReadCompletedOperation "+readCompletedOperation.id+": _syncObjectDescriptorRawDataFromReadCompletedOperation from "+readCompletedOperation.rawDataService.identifier+" Iterate Raw Data "+i+", referrer "+readCompletedOperation.referrer.id+", for "+readCompletedOperation.referrer.target.name + (readCompletedOperation.referrer?.data?.readExpressions? (" "+readCompletedOperation.referrer?.data?.readExpressions) : "") + " like "+ readCompletedOperation.referrer.criteria); //TODO Test if rawData[i] is already a mapped object and, if so, set iMappingResult to Promise.resolve(rawData[i]) // WARNING: We might need to add logic inside _syncObjectDescriptorRawDataFromReadCompletedOperation @@ -1190,7 +1195,7 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu //Now that all is done, we're saving: return promise.then( () => { - this._logTypeEvent(readCompletedOperation.referrer.target, "<<<<<<<<<<<<<<<<<<<<<<<<<<<-> Service capture ReadCompletedOperation "+readCompletedOperation.id+": saving changes from "+readCompletedOperation.rawDataService.identifier+", referrer "+readCompletedOperation.referrer.id+", for "+readCompletedOperation.referrer.target.name + (readCompletedOperation.referrer?.data?.readExpressions? (" "+readCompletedOperation.referrer?.data?.readExpressions) : "") + " like "+ readCompletedOperation.referrer.criteria); + this._logTypeEvent(readCompletedOperation.referrer.target, "<<<<<<<<<<<<<<<<<<<<<<<<<<<-> Service capture ReadCompletedOperation POST COMPLETED"+readCompletedOperation.id+": saving changes from "+readCompletedOperation.rawDataService.identifier+", referrer "+readCompletedOperation.referrer.id+", for "+readCompletedOperation.referrer.target.name + (readCompletedOperation.referrer?.data?.readExpressions? (" "+readCompletedOperation.referrer?.data?.readExpressions) : "") + " like "+ readCompletedOperation.referrer.criteria); /* one more thing: if it's a readOperation for a relationship, we need to make sure the join actually happens. @@ -1344,7 +1349,7 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu } - this._logTypeEvent(readCompletedOperation.referrer?.target, "Capture ReadCompletedOperation "+readCompletedOperation.id+" from "+readCompletedOperation.rawDataService.identifier+", referrer "+readCompletedOperation.referrer?.id+", for "+readCompletedOperation.referrer?.target.name + (readCompletedOperation.referrer?.data?.readExpressions? (" "+readCompletedOperation.referrer?.data?.readExpressions) : "") + " like "+ readCompletedOperation.referrer?.criteria+": ", readCompletedOperation.data); + this._logTypeEvent(readCompletedOperation.referrer?.target, "Capture ReadCompletedOperation BEGIN "+readCompletedOperation.id+" from "+readCompletedOperation.rawDataService.identifier+", referrer "+readCompletedOperation.referrer?.id+", for "+readCompletedOperation.referrer?.target.name + (readCompletedOperation.referrer?.data?.readExpressions? (" "+readCompletedOperation.referrer?.data?.readExpressions) : "") + " like "+ readCompletedOperation.referrer?.criteria+": ", readCompletedOperation.data); //Record the read completion from that service: if(readCompletedOperation.type === ReadCompletedOperationType) { @@ -1631,6 +1636,10 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu unregisterReadOperation(aReadOperation) { this._childDataServiceReadCompletionOperationByReadOperation.delete(aReadOperation); + // if (aReadOperation.rawDataService) { + + // } + // aReadOperation.hints.rawDataService.unregisterPendingDataOperation(aReadOperation); } captureSynchronizationDataServiceReadFailedOperation(readFailedOperation) { @@ -1653,7 +1662,7 @@ exports.SynchronizationDataService = class SynchronizationDataService extends Mu this.registerChildDataServiceReadCompletionOperation(readFailedOperation); - this._logTypeEvent(readFailedOperation.referrer.target, "Sync Service capture ReadFailedOperation "+readFailedOperation.id+" from "+readFailedOperation.rawDataService.identifier+", referrer "+readFailedOperation.referrer.id+", for "+readFailedOperation.referrer.target.name + (readFailedOperation.referrer?.data?.readExpressions? (" "+readFailedOperation.referrer?.data?.readExpressions) : "") + " like "+ readFailedOperation.referrer.criteria+": ", readFailedOperation.data); + // this._logTypeEvent(readFailedOperation.referrer.target, "Sync Service capture ReadFailedOperation "+readFailedOperation.id+" from "+readFailedOperation.rawDataService.identifier+", referrer "+readFailedOperation.referrer.id+", for "+readFailedOperation.referrer.target.name + (readFailedOperation.referrer?.data?.readExpressions? (" "+readFailedOperation.referrer?.data?.readExpressions) : "") + " like "+ readFailedOperation.referrer.criteria+": ", readFailedOperation.data); if((readFailedOperation.data.name === DataOperationErrorNames.DatabaseMissing) || (readFailedOperation.data.name === DataOperationErrorNames.ObjectDescriptorStoreMissing) ) { diff --git a/data/service/raw-data-service.js b/data/service/raw-data-service.js index 531180541..dfd86efc0 100644 --- a/data/service/raw-data-service.js +++ b/data/service/raw-data-service.js @@ -1535,6 +1535,10 @@ RawDataService.addClassProperties({ return Promise.resolveNull; } + if (type.name === "EmploymentPositionStaffing") { + console.log("RawDataService.resolveObjectForTypeRawData", type.name, rawData, context); + } + //Retrieves an existing object is responsible data service is uniquing, or creates one object = this.getDataObject(type, rawData, dataIdentifier, context); @@ -1555,6 +1559,9 @@ RawDataService.addClassProperties({ if (Promise.is(result)) { return result.then(function () { + if (type.name === "EmploymentPositionStaffing") { + console.log("RawDataService.resolveObjectForTypeRawData DONE", type.name, rawData, context); + } return object; }); } else { @@ -3345,6 +3352,9 @@ RawDataService.addClassProperties({ dataOperationRegistration = { dataOperation: dataOperation }; + if (dataOperation.target.name === "Person") { + console.log("RawDataService.registerPendingDataOperationWithContext", this.name, dataOperation.id, dataOperation); + } if (context) { dataOperationRegistration.context = context; @@ -3366,6 +3376,13 @@ RawDataService.addClassProperties({ unregisterPendingDataOperation: { value: function (dataOperation) { + let registration = this._pendingDataOperationById.get(dataOperation.id); + if (dataOperation.target.name === "Person") { + console.log("RawDataService.unregisterPendingDataOperation", dataOperation.id, !!registration, dataOperation) + } + if (registration) { + registration.completionPromiseResolve(); + } this._pendingDataOperationById.delete(dataOperation.id); //Backup until bug fixed //DataService.mainService.unregisterDataStreamForDataOperation(dataOperation); @@ -3374,7 +3391,15 @@ RawDataService.addClassProperties({ unregisterDataOperationPendingReferrer: { value: function (dataOperation) { + let registration = this._pendingDataOperationById.get(dataOperation.referrerId); + // if (dataOperation.target.name === "Person") { + // console.log("RawDataService.unregisterPendingDataOperationReferrer", dataOperation.id, dataOperation.referrerId, !!registration, dataOperation) + // } + if (registration) { + registration.completionPromiseResolve(); + } this._pendingDataOperationById.delete(dataOperation.referrerId); + //Backup until bug fixed //DataService.mainService.unregisterDataStreamForDataOperation(dataOperation); } @@ -3770,16 +3795,28 @@ RawDataService.addClassProperties({ this.rawDataDone(stream); //this._thenableByOperationId.delete(operation.referrerId); this.unregisterDataOperationPendingReferrer(operation); + } else { + this.unregisterDataOperationPendingReferrer(operation); } // else { // console.log("receiving operation of type:"+operation.type+", but can't find a matching stream"); // } //console.log("handleReadCompleted -clear _thenableByOperationId- referrerId: ",operation.referrerId); + } else { + } } }, + relevantOperationForResponse: { + value: function (readOperation) { + let referrer = readOperation.referrer; + // return referrer && referrer.type !== "readCompletedOperation" ? referrer : readOperation; + return referrer && (referrer.type !== DataOperation.Type.ReadCompletedOperation && referrer.type !== DataOperation.Type.ReadFailedOperation) ? referrer : readOperation; + } + }, + handleReadFailedOperation: { value: function (operation) { var stream = this.referrerContextForDataOperation(operation); diff --git a/data/service/serialized-data-service.mod/serialized-data-service.js b/data/service/serialized-data-service.mod/serialized-data-service.js index 33b84d0db..977a2b4f8 100644 --- a/data/service/serialized-data-service.mod/serialized-data-service.js +++ b/data/service/serialized-data-service.mod/serialized-data-service.js @@ -333,7 +333,7 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi } - _objectPromiseForDataIdentifier(aDataIdentifier, mainService) { + _objectPromiseForDataIdentifier(aDataIdentifier, mainService, dataOperation) { let iObjectValue = mainService.objectForDataIdentifier(aDataIdentifier); if(!iObjectValue) { @@ -342,6 +342,11 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi iObjectValueQuery = DataQuery.withTypeAndCriteria(aDataIdentifier.objectDescriptor, criteria); + if (dataOperation) { + iObjectValueQuery.hints = {referrerOperation: dataOperation}; + } + + return mainService.fetchData(iObjectValueQuery).catch((e) => { //FIXME: This should not be necessary. SynchronizationDataService should be able to //understand that fetching by identifier here means fetching by id in PostgreSQL @@ -363,7 +368,7 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi } /* adds a promise resolving to the value to mappingPromises passed in*/ - _mapRawDataPropertyToObject (record, property, object, mappingPromises, mainService) { + _mapRawDataPropertyToObject (record, property, object, mappingPromises, mainService, context) { let objectDescriptor = object.objectDescriptor, iPropertyDescriptor = objectDescriptor.propertyDescriptorNamed(property); @@ -376,10 +381,10 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi if(iPropertyDescriptor.valueDescriptor) { if (this._isAsync(iPropertyDescriptor.valueDescriptor)) { mappingPromises.push(iPropertyDescriptor.valueDescriptor.then((valueDescriptor) => { - return this.__mapRawDataPropertyToObject(record, property, valueDescriptor, object, mainService); + return this.__mapRawDataPropertyToObject(record, property, valueDescriptor, object, mainService, context); })); } else { - return this.__mapRawDataPropertyToObject(record, property, iPropertyDescriptor.valueDescriptor, object, mainService); + return this.__mapRawDataPropertyToObject(record, property, iPropertyDescriptor.valueDescriptor, object, mainService, context); } } else { @@ -389,20 +394,39 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi } } - __mapRawDataPropertyToObject (record, property, valueDescriptor, object, mainService) { + __mapRawDataPropertyToObject (record, property, valueDescriptor, object, mainService, context) { let objectDescriptor = object.objectDescriptor, - iPropertyDescriptor = objectDescriptor.propertyDescriptorNamed(property); + iPropertyDescriptor = objectDescriptor.propertyDescriptorNamed(property), + dataOperation = context instanceof DataOperation ? context : null; + + let trackMapping = object.objectDescriptor.name === "IncorporatedOrganization" || object.objectDescriptor.name === "Organization" || object.objectDescriptor.name === "JobRole"; + + if (trackMapping) { + window.incOrgProperties = window.incOrgProperties || new Map(); + // debugger + if (!window.incOrgProperties.has(object)) { + window.incOrgProperties.set(object, new Set()); + } + window.incOrgProperties.get(object).add(property); + } if(iPropertyDescriptor.cardinality === 1) { let iPropertyValue = object[property]; if(typeof iPropertyValue === "string" /* would sure be handy to actually have a uuid tye right now...*/) { let aDataIdentifier = this.dataIdentifierForTypePrimaryKey(iPropertyDescriptor.valueDescriptor, record.identifier); - return this._objectPromiseForDataIdentifier(aDataIdentifier, mainService) + return this._objectPromiseForDataIdentifier(aDataIdentifier, mainService, dataOperation) .then((iObjectValue) => { + if (trackMapping) { + window.incOrgProperties.get(object).delete(property); + } + object[property] = iObjectValue }) } else { + if (trackMapping) { + window.incOrgProperties.get(object).delete(property); + } object[property] = record[property]; } } else { @@ -427,10 +451,13 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi let aDataIdentifier = this.dataIdentifierForTypePrimaryKey(objectDescriptor, iPropertyValues[i]); mappingPromises.push( - this._objectPromiseForDataIdentifier(aDataIdentifier, mainService) + this._objectPromiseForDataIdentifier(aDataIdentifier, mainService, dataOperation) ); } return Promise.all(mappingPromises).then((values) => { + if (trackMapping) { + window.incOrgProperties.get(object).delete(property); + } object[property].splice.apply(object[property], [0, Infinity].concat(values.filter((value) => !!value))); return; }); @@ -439,6 +466,9 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi throw "mapObjectToRawData for a property that is Map needs to be implemented"; } } else { + if (trackMapping) { + window.incOrgProperties.get(object).delete(property); + } object[property] = iPropertyValues; } } @@ -452,8 +482,10 @@ exports.SerializedDataService = class SerializedDataService extends RawDataServi mappingPromises = []; + + for(let countI = recordKeys.length, i = 0; (i { var serializedOperation = this._serializer.serializeObject(operation); + var operationDataKBSize = sizeof(serializedOperation) / 1024; //console.debug("----> send "+operationDataKBSize+" KB operation "+serializedOperation); diff --git a/worker/data-worker.js b/worker/data-worker.js index fb5286d56..b77fc64e0 100644 --- a/worker/data-worker.js +++ b/worker/data-worker.js @@ -606,10 +606,12 @@ exports.DataWorker = Worker.specialize( /** @lends DataWorker.prototype */{ try { var deserializedOperationPromise = this.deserializer.deserializeObject(); } catch (ex) { + console.error(ex); deserializedOperationPromise = this.tryToConvertMessageToDataOperation(message); } deserializedOperationPromise.catch((error) => { + console.error(error); return this.tryToConvertMessageToDataOperation(message); }) .then((deserializedOperation) => { @@ -643,8 +645,8 @@ exports.DataWorker = Worker.specialize( /** @lends DataWorker.prototype */{ }) } } else { - console.error("No deserialization for ",serializedOperation); - return Promise.reject("Unknown message: ",serializedOperation); + console.error("No deserialization for ",message); + return Promise.reject("Unknown message: ",message); } });