Merge branch 'open338' into rems_data

This commit is contained in:
Henry 2015-12-02 19:10:33 -08:00
commit 7fad2f6f2e
10 changed files with 203 additions and 46 deletions

View File

@ -102,6 +102,12 @@
"implementation": "navigation/NavigationService.js" "implementation": "navigation/NavigationService.js"
} }
], ],
"policies": [
{
"implementation": "creation/CreationPolicy.js",
"category": "creation"
}
],
"actions": [ "actions": [
{ {
"key": "navigate", "key": "navigate",

View File

@ -69,7 +69,7 @@ define(
// Introduce one create action per type // Introduce one create action per type
return this.typeService.listTypes().filter(function (type) { return this.typeService.listTypes().filter(function (type) {
return type.hasFeature("creation"); return self.policyService.allow("creation", type);
}).map(function (type) { }).map(function (type) {
return new CreateAction( return new CreateAction(
type, type,

View File

@ -0,0 +1,49 @@
/*****************************************************************************
* 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*/
define(
[],
function () {
"use strict";
/**
* A policy for determining whether objects of a certain type can be
* created.
* @returns {{allow: Function}}
* @constructor
*/
function CreationPolicy() {
return {
/**
* Only allow creation of object types that have the
* Creation capability
*/
allow: function (type) {
return type.hasFeature("creation");
}
};
}
return CreationPolicy;
}
);

View File

@ -33,6 +33,9 @@ define(
var mockTypeService, var mockTypeService,
mockDialogService, mockDialogService,
mockCreationService, mockCreationService,
mockPolicyService,
mockCreationPolicy,
mockPolicyMap = {},
mockTypes, mockTypes,
provider; provider;
@ -67,14 +70,32 @@ define(
"creationService", "creationService",
[ "createObject" ] [ "createObject" ]
); );
mockPolicyService = jasmine.createSpyObj(
"policyService",
[ "allow" ]
);
mockTypes = [ "A", "B", "C" ].map(createMockType); mockTypes = [ "A", "B", "C" ].map(createMockType);
mockTypes.forEach(function(type){
mockPolicyMap[type.getName()] = true;
});
mockCreationPolicy = function(type){
return mockPolicyMap[type.getName()];
};
mockPolicyService.allow.andCallFake(function(category, type){
return category === "creation" && mockCreationPolicy(type) ? true : false;
});
mockTypeService.listTypes.andReturn(mockTypes); mockTypeService.listTypes.andReturn(mockTypes);
provider = new CreateActionProvider( provider = new CreateActionProvider(
mockTypeService, mockTypeService,
mockDialogService, mockDialogService,
mockCreationService mockCreationService,
mockPolicyService
); );
}); });
@ -94,15 +115,15 @@ define(
it("does not expose non-creatable types", function () { it("does not expose non-creatable types", function () {
// One of the types won't have the creation feature... // One of the types won't have the creation feature...
mockTypes[1].hasFeature.andReturn(false); mockPolicyMap[mockTypes[0].getName()] = false;
// ...so it should have been filtered out. // ...so it should have been filtered out.
expect(provider.getActions({ expect(provider.getActions({
key: "create", key: "create",
domainObject: {} domainObject: {}
}).length).toEqual(2); }).length).toEqual(2);
// Make sure it was creation which was used to check // Make sure it was creation which was used to check
expect(mockTypes[1].hasFeature) expect(mockPolicyService.allow)
.toHaveBeenCalledWith("creation"); .toHaveBeenCalledWith("creation", mockTypes[0]);
}); });
}); });
} }

View File

@ -0,0 +1,53 @@
/*****************************************************************************
* 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,it,expect,beforeEach,jasmine*/
define(
["../../src/creation/CreationPolicy"],
function (CreationPolicy) {
"use strict";
describe("The creation policy", function () {
var mockType,
policy;
beforeEach(function () {
mockType = jasmine.createSpyObj(
'type',
['hasFeature']
);
policy = new CreationPolicy();
});
it("allows creation of types with the creation feature", function () {
mockType.hasFeature.andReturn(true);
expect(policy.allow(mockType)).toBeTruthy();
});
it("disallows creation of types without the creation feature", function () {
mockType.hasFeature.andReturn(false);
expect(policy.allow(mockType)).toBeFalsy();
});
});
}
);

View File

@ -8,6 +8,7 @@
"creation/CreateMenuController", "creation/CreateMenuController",
"creation/CreateWizard", "creation/CreateWizard",
"creation/CreationService", "creation/CreationService",
"creation/CreationPolicy",
"creation/LocatorController", "creation/LocatorController",
"navigation/NavigateAction", "navigation/NavigateAction",
"navigation/NavigationService", "navigation/NavigationService",

View File

@ -28,9 +28,7 @@ define(
var DISALLOWED_ACTIONS = [ var DISALLOWED_ACTIONS = [
"move", "move",
"copy", "copy"
"link",
"compose"
]; ];
/** /**

View File

@ -71,7 +71,7 @@ define(
*/ */
CopyService.prototype.perform = function (domainObject, parent) { CopyService.prototype.perform = function (domainObject, parent) {
var $q = this.$q, var $q = this.$q,
copyTask = new CopyTask(domainObject, parent, this.persistenceService, this.$q, this.now); copyTask = new CopyTask(domainObject, parent, this.persistenceService, this.policyService, this.$q, this.now);
if (this.validate(domainObject, parent)) { if (this.validate(domainObject, parent)) {
return copyTask.perform(); return copyTask.perform();
} else { } else {

View File

@ -38,12 +38,14 @@ define(
* @param now * @param now
* @constructor * @constructor
*/ */
function CopyTask (domainObject, parent, persistenceService, $q, now){ function CopyTask (domainObject, parent, persistenceService, policyService, $q, now){
this.domainObject = domainObject; this.domainObject = domainObject;
this.parent = parent; this.parent = parent;
this.$q = $q; this.$q = $q;
this.deferred = undefined; this.deferred = undefined;
this.persistenceService = persistenceService; this.persistenceService = persistenceService;
this.policyService = policyService;
this.persistenceSpace = parent.getCapability("persistence") && parent.getCapability("persistence").getSpace();
this.persisted = 0; this.persisted = 0;
this.now = now; this.now = now;
this.clones = []; this.clones = [];
@ -52,17 +54,31 @@ define(
function composeChild(child, parent) { function composeChild(child, parent) {
//Once copied, associate each cloned //Once copied, associate each cloned
// composee with its parent clone // composee with its parent clone
child.model.location = parent.id;
parent.model.composition = parent.model.composition || []; parent.getModel().composition.push(child.getId());
return parent.model.composition.push(child.id);
//Check if the object being composed is a link
if (!child.getCapability("location").isLink()) {
child.getModel().location = parent.getId();
}
} }
function cloneObjectModel(objectModel) { function cloneObjectModel(objectModel) {
var clone = JSON.parse(JSON.stringify(objectModel)); var clone = JSON.parse(JSON.stringify(objectModel));
delete clone.composition; /**
* Reset certain fields.
*/
//If has a composition, set it to an empty array. Will be
// recomposed later with the ids of its cloned children.
if (clone.composition) {
//Important to set it to an empty array here, otherwise
// hasCapability("composition") returns false;
clone.composition = [];
}
delete clone.persisted; delete clone.persisted;
delete clone.modified; delete clone.modified;
delete clone.location;
return clone; return clone;
} }
@ -73,13 +89,10 @@ define(
* result in automatic request batching by the browser. * result in automatic request batching by the browser.
*/ */
function persistObjects(self) { function persistObjects(self) {
return self.$q.all(self.clones.map(function(clone){ return self.$q.all(self.clones.map(function(clone){
clone.model.persisted = self.now(); return clone.getCapability("persistence").persist().then(function(){
return self.persistenceService.createObject(clone.persistenceSpace, clone.id, clone.model) self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
.then(function(){ });
self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
});
})).then(function(){ })).then(function(){
return self; return self;
}); });
@ -91,15 +104,13 @@ define(
function addClonesToParent(self) { function addClonesToParent(self) {
var parentClone = self.clones[self.clones.length-1]; var parentClone = self.clones[self.clones.length-1];
if (!self.parent.hasCapability('composition')){ //self.persistenceService
return self.$q.reject(); // .updateObject(self.persistenceSpace,
} // parentClone.id, parentClone.model)
return parentClone.getCapability("persistence").persist()
return self.persistenceService .then(function(){self.parent.getCapability("composition").add(parentClone.getId())})
.updateObject(parentClone.persistenceSpace, parentClone.id, parentClone.model) .then(function(){return self.parent.getCapability("persistence").persist();})
.then(function(){return self.parent.getCapability("composition").add(parentClone.id);}) .then(function(){return parentClone;});
.then(function(){return self.parent.getCapability("persistence").persist();})
.then(function(){return parentClone;});
// Ensure the clone of the original domainObject is returned // Ensure the clone of the original domainObject is returned
} }
@ -118,7 +129,7 @@ define(
return promise.then(function(){ return promise.then(function(){
// ...to recursively copy it (and its children) // ...to recursively copy it (and its children)
return self.copy(composee, originalParent).then(function(composee){ return self.copy(composee, originalParent).then(function(composee){
composeChild(composee, clonedParent); return composeChild(composee, clonedParent);
}); });
});}, self.$q.when(undefined) });}, self.$q.when(undefined)
); );
@ -137,23 +148,37 @@ define(
*/ */
CopyTask.prototype.copy = function(originalObject, originalParent) { CopyTask.prototype.copy = function(originalObject, originalParent) {
var self = this, var self = this,
modelClone = { clone;
id: uuid(),
model: cloneObjectModel(originalObject.getModel()),
persistenceSpace: originalParent.hasCapability('persistence') && originalParent.getCapability('persistence').getSpace()
};
return this.$q.when(originalObject.useCapability('composition')).then(function(composees){ //Check if the type of the object being copied allows for
self.deferred.notify({phase: "preparing"}); // creation of new instances. If it does not, then a link to the
//Duplicate the object's children, and their children, and // original will be created instead.
// so on down to the leaf nodes of the tree. if (this.policyService.allow("creation", originalObject.getCapability("type"))){
return self.copyComposees(composees, modelClone, originalObject).then(function (){ //create a new clone of the original object. Use the
//Add the clone to the list of clones that will // creation capability of the targetParent to create the
//be returned by this function // new clone. This will ensure that the correct persistence
self.clones.push(modelClone); // space is used.
return modelClone; clone = this.parent.hasCapability("instantiation") && originalParent.useCapability("instantiation", cloneObjectModel(originalObject.getModel()));
//Iterate through child tree
return this.$q.when(originalObject.useCapability('composition')).then(function(composees){
self.deferred.notify({phase: "preparing"});
//Duplicate the object's children, and their children, and
// so on down to the leaf nodes of the tree.
//If it is a link, don't both with children
return self.copyComposees(composees, clone, originalObject).then(function (){
//Add the clone to the list of clones that will
//be returned by this function
self.clones.push(clone);
return clone;
});
}); });
}); } else {
//Creating a link, no need to iterate children
return $q.when(originalObject);
}
}; };
/** /**
@ -185,6 +210,10 @@ define(
CopyTask.prototype.perform = function(){ CopyTask.prototype.perform = function(){
this.deferred = this.$q.defer(); this.deferred = this.$q.defer();
if (!this.parent.hasCapability('composition')){
return self.$q.reject();
}
this.buildCopyPlan() this.buildCopyPlan()
.then(persistObjects) .then(persistObjects)
.then(addClonesToParent) .then(addClonesToParent)

View File

@ -72,7 +72,7 @@ define(
policy = new CrossSpacePolicy(); policy = new CrossSpacePolicy();
}); });
['move', 'copy', 'link', 'compose'].forEach(function (key) { ['move', 'copy'].forEach(function (key) {
describe("for " + key + " actions", function () { describe("for " + key + " actions", function () {
beforeEach(function () { beforeEach(function () {
testActionMetadata.key = key; testActionMetadata.key = key;