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"
}
],
"policies": [
{
"implementation": "creation/CreationPolicy.js",
"category": "creation"
}
],
"actions": [
{
"key": "navigate",

View File

@ -69,7 +69,7 @@ define(
// Introduce one create action per type
return this.typeService.listTypes().filter(function (type) {
return type.hasFeature("creation");
return self.policyService.allow("creation", type);
}).map(function (type) {
return new CreateAction(
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,
mockDialogService,
mockCreationService,
mockPolicyService,
mockCreationPolicy,
mockPolicyMap = {},
mockTypes,
provider;
@ -67,14 +70,32 @@ define(
"creationService",
[ "createObject" ]
);
mockPolicyService = jasmine.createSpyObj(
"policyService",
[ "allow" ]
);
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);
provider = new CreateActionProvider(
mockTypeService,
mockDialogService,
mockCreationService
mockCreationService,
mockPolicyService
);
});
@ -94,15 +115,15 @@ define(
it("does not expose non-creatable types", function () {
// 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.
expect(provider.getActions({
key: "create",
domainObject: {}
}).length).toEqual(2);
// Make sure it was creation which was used to check
expect(mockTypes[1].hasFeature)
.toHaveBeenCalledWith("creation");
expect(mockPolicyService.allow)
.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/CreateWizard",
"creation/CreationService",
"creation/CreationPolicy",
"creation/LocatorController",
"navigation/NavigateAction",
"navigation/NavigationService",

View File

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

View File

@ -71,7 +71,7 @@ define(
*/
CopyService.prototype.perform = function (domainObject, parent) {
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)) {
return copyTask.perform();
} else {

View File

@ -38,12 +38,14 @@ define(
* @param now
* @constructor
*/
function CopyTask (domainObject, parent, persistenceService, $q, now){
function CopyTask (domainObject, parent, persistenceService, policyService, $q, now){
this.domainObject = domainObject;
this.parent = parent;
this.$q = $q;
this.deferred = undefined;
this.persistenceService = persistenceService;
this.policyService = policyService;
this.persistenceSpace = parent.getCapability("persistence") && parent.getCapability("persistence").getSpace();
this.persisted = 0;
this.now = now;
this.clones = [];
@ -52,17 +54,31 @@ define(
function composeChild(child, parent) {
//Once copied, associate each cloned
// composee with its parent clone
child.model.location = parent.id;
parent.model.composition = parent.model.composition || [];
return parent.model.composition.push(child.id);
parent.getModel().composition.push(child.getId());
//Check if the object being composed is a link
if (!child.getCapability("location").isLink()) {
child.getModel().location = parent.getId();
}
}
function cloneObjectModel(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.modified;
delete clone.location;
return clone;
}
@ -73,13 +89,10 @@ define(
* result in automatic request batching by the browser.
*/
function persistObjects(self) {
return self.$q.all(self.clones.map(function(clone){
clone.model.persisted = self.now();
return self.persistenceService.createObject(clone.persistenceSpace, clone.id, clone.model)
.then(function(){
self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
});
return clone.getCapability("persistence").persist().then(function(){
self.deferred.notify({phase: "copying", totalObjects: self.clones.length, processed: ++self.persisted});
});
})).then(function(){
return self;
});
@ -91,15 +104,13 @@ define(
function addClonesToParent(self) {
var parentClone = self.clones[self.clones.length-1];
if (!self.parent.hasCapability('composition')){
return self.$q.reject();
}
return self.persistenceService
.updateObject(parentClone.persistenceSpace, parentClone.id, parentClone.model)
.then(function(){return self.parent.getCapability("composition").add(parentClone.id);})
.then(function(){return self.parent.getCapability("persistence").persist();})
.then(function(){return parentClone;});
//self.persistenceService
// .updateObject(self.persistenceSpace,
// parentClone.id, parentClone.model)
return parentClone.getCapability("persistence").persist()
.then(function(){self.parent.getCapability("composition").add(parentClone.getId())})
.then(function(){return self.parent.getCapability("persistence").persist();})
.then(function(){return parentClone;});
// Ensure the clone of the original domainObject is returned
}
@ -118,7 +129,7 @@ define(
return promise.then(function(){
// ...to recursively copy it (and its children)
return self.copy(composee, originalParent).then(function(composee){
composeChild(composee, clonedParent);
return composeChild(composee, clonedParent);
});
});}, self.$q.when(undefined)
);
@ -137,23 +148,37 @@ define(
*/
CopyTask.prototype.copy = function(originalObject, originalParent) {
var self = this,
modelClone = {
id: uuid(),
model: cloneObjectModel(originalObject.getModel()),
persistenceSpace: originalParent.hasCapability('persistence') && originalParent.getCapability('persistence').getSpace()
};
clone;
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.
return self.copyComposees(composees, modelClone, originalObject).then(function (){
//Add the clone to the list of clones that will
//be returned by this function
self.clones.push(modelClone);
return modelClone;
//Check if the type of the object being copied allows for
// creation of new instances. If it does not, then a link to the
// original will be created instead.
if (this.policyService.allow("creation", originalObject.getCapability("type"))){
//create a new clone of the original object. Use the
// creation capability of the targetParent to create the
// new clone. This will ensure that the correct persistence
// space is used.
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(){
this.deferred = this.$q.defer();
if (!this.parent.hasCapability('composition')){
return self.$q.reject();
}
this.buildCopyPlan()
.then(persistObjects)
.then(addClonesToParent)

View File

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