From e32feb29e22a337472a7795604219a2dbb9d6042 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 8 Dec 2015 15:41:33 -0800 Subject: [PATCH 1/3] [Duplicate] Rewrite identifiers in clones Traverse object models of clones and rewrite domain object identifiers that have changed during duplication. Addresses https://github.com/nasa/openmctweb/issues/332 --- .../entanglement/src/services/CopyTask.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/platform/entanglement/src/services/CopyTask.js b/platform/entanglement/src/services/CopyTask.js index d20c233b42..08bced4db8 100644 --- a/platform/entanglement/src/services/CopyTask.js +++ b/platform/entanglement/src/services/CopyTask.js @@ -45,6 +45,7 @@ define( this.policyService = policyService; this.persisted = 0; this.clones = []; + this.idMap = {}; } function composeChild(child, parent, setLocation) { @@ -104,6 +105,40 @@ define( .then(function(){return self.firstClone;}); } + /** + * Update identifiers in a cloned object model (or part of + * a cloned object model) to reflect new identifiers after + * copying. + * @private + */ + CopyTask.prototype.rewriteIdentifiers = function (obj) { + var idMap = this.idMap; + + if (Array.isArray(obj)) { + obj.forEach(function (value, index) { + obj[index] = idMap[value] || value; + this.rewriteIdentifiers(obj[index]); + }, this); + } else if (obj && typeof obj === 'object') { + Object.keys(obj).forEach(function (key) { + var value = obj[key]; + obj[key] = idMap[value] || value; + if (idMap[key]) { + delete obj[key]; + obj[idMap[key]] = value; + } + this.rewriteIdentifiers(value); + }, this); + } + }; + + function rewriteIdentifiersInClones(self) { + self.clones.forEach(function (clone) { + self.rewriteIdentifiers(clone.getModel()); + }); + return self; + } + /** * Given an array of objects composed by a parent, clone them, then * add them to the parent. @@ -153,6 +188,9 @@ define( // space is used. clone = this.parent.useCapability("instantiation", cloneObjectModel(originalObject.getModel())); + // Record ID mappings so we can rewrite properties later + self.idMap[originalObject.getId()] = clone.getId(); + //Iterate through child tree return this.$q.when(originalObject.useCapability('composition')).then(function(composees){ self.deferred.notify({phase: "preparing"}); @@ -207,6 +245,7 @@ define( this.deferred = this.$q.defer(); this.buildCopyPlan() + .then(rewriteIdentifiersInClones) .then(persistObjects) .then(addClonesToParent) .then(this.deferred.resolve, this.deferred.reject); From 46f9b31cff6a340441ee38006c61b713a9dfc1e0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 8 Dec 2015 16:27:46 -0800 Subject: [PATCH 2/3] [Duplicate] Test rewriting of identifiers --- .../test/services/CopyTaskSpec.js | 187 ++++++++++++++++++ platform/entanglement/test/suite.json | 1 + 2 files changed, 188 insertions(+) create mode 100644 platform/entanglement/test/services/CopyTaskSpec.js diff --git a/platform/entanglement/test/services/CopyTaskSpec.js b/platform/entanglement/test/services/CopyTaskSpec.js new file mode 100644 index 0000000000..303958d2c3 --- /dev/null +++ b/platform/entanglement/test/services/CopyTaskSpec.js @@ -0,0 +1,187 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +/*global define,describe,beforeEach,it,jasmine,expect,spyOn */ + +define( + [ + '../../src/services/CopyTask', + '../DomainObjectFactory' + ], + function (CopyTask, domainObjectFactory) { + 'use strict'; + + var ID_A = "some-string-with-vaguely-uuidish-uniqueness", + ID_B = "some-other-similarly-unique-string"; + + function synchronousPromise(value) { + return (value && value.then) ? value : { + then: function (callback) { + return synchronousPromise(callback(value)); + } + }; + } + + describe("CopyTask", function () { + var mockDomainObject, + mockParentObject, + mockPolicyService, + mockQ, + mockDeferred, + testModel, + mockCallback, + counter, + cloneIds, + task; + + function makeMockCapabilities(childIds) { + var mockCapabilities = { + persistence: jasmine.createSpyObj( + 'persistence', + ['persist'] + ), + composition: jasmine.createSpyObj( + 'composition', + ['add', 'invoke'] + ), + instantiation: jasmine.createSpyObj( + 'instantiation', + ['instantiate', 'invoke'] + ) + }, + mockChildren = (childIds || []).map(function (id) { + return domainObjectFactory({ + id: id, + capabilities: makeMockCapabilities([]), + model: { originalId: id } + }); + }); + + mockCapabilities.persistence.persist + .andReturn(synchronousPromise(true)); + mockCapabilities.composition.add.andCallFake(function (obj) { + return synchronousPromise(obj); + }); + mockCapabilities.composition.invoke + .andReturn(synchronousPromise(mockChildren)); + mockCapabilities.instantiation.invoke + .andCallFake(function (model) { + var id = "some-id-" + counter; + cloneIds[model.originalId] = id; + counter += 1; + return domainObjectFactory({ + id: id, + model: model, + capabilities: makeMockCapabilities() + }); + }); + + return mockCapabilities; + } + + beforeEach(function () { + counter = 0; + cloneIds = {}; + + testModel = { + composition: [ ID_A, ID_B ], + someObj: {}, + someArr: [ ID_A, ID_B ] + }; + testModel.someObj[ID_A] = "some value"; + testModel.someObj.someProperty = ID_B; + + mockDomainObject = domainObjectFactory({ + capabilities: makeMockCapabilities(testModel.composition), + model: testModel + }); + mockParentObject = domainObjectFactory({ + capabilities: makeMockCapabilities() + }); + mockPolicyService = jasmine.createSpyObj( + 'policyService', + [ 'allow' ] + ); + mockQ = jasmine.createSpyObj('$q', ['when', 'defer', 'all']); + mockDeferred = jasmine.createSpyObj( + 'deferred', + [ 'notify', 'resolve', 'reject' ] + ); + + mockPolicyService.allow.andReturn(true); + + mockQ.when.andCallFake(synchronousPromise); + mockQ.defer.andReturn(mockDeferred); + mockQ.all.andCallFake(function (promises) { + return synchronousPromise(promises.map(function (promise) { + var value; + promise.then(function (v) { value = v; }); + return value; + })); + }); + + mockDeferred.resolve.andCallFake(function (value) { + mockDeferred.promise = synchronousPromise(value); + }); + + task = new CopyTask( + mockDomainObject, + mockParentObject, + mockPolicyService, + mockQ + ); + }); + + + describe("produces models which", function () { + var model; + + beforeEach(function () { + task.perform().then(function (clone) { + model = clone.getModel(); + }); + }); + + it("contain rewritten identifiers in arrays", function () { + expect(model.someArr) + .toEqual(testModel.someArr.map(function (id) { + return cloneIds[id]; + })); + }); + + it("contain rewritten identifiers in properties", function () { + expect(model.someObj.someProperty) + .toEqual(cloneIds[testModel.someObj.someProperty]); + }); + + + it("contain rewritten identifiers in property names", function () { + expect(model.someObj[cloneIds[ID_A]]) + .toEqual(testModel.someObj[ID_A]); + }); + }); + + }); + + + } +); \ No newline at end of file diff --git a/platform/entanglement/test/suite.json b/platform/entanglement/test/suite.json index 243da94c39..223e473629 100644 --- a/platform/entanglement/test/suite.json +++ b/platform/entanglement/test/suite.json @@ -7,6 +7,7 @@ "actions/SetPrimaryLocationAction", "policies/CrossSpacePolicy", "services/CopyService", + "services/CopyTask", "services/LinkService", "services/MoveService", "services/LocationService", From 19f07aa398e93a32efa937b07b7293d245c4c676 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 8 Dec 2015 16:34:36 -0800 Subject: [PATCH 3/3] [Duplicate] Add trailing newline --- platform/entanglement/test/services/CopyTaskSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/entanglement/test/services/CopyTaskSpec.js b/platform/entanglement/test/services/CopyTaskSpec.js index 303958d2c3..e5a2c57f2b 100644 --- a/platform/entanglement/test/services/CopyTaskSpec.js +++ b/platform/entanglement/test/services/CopyTaskSpec.js @@ -184,4 +184,4 @@ define( } -); \ No newline at end of file +);