mirror of
https://github.com/nasa/openmct.git
synced 2025-06-14 05:08:15 +00:00
Merge pull request #1448 from nasa/update-composition-policy
Update composition policy
This commit is contained in:
@ -2261,10 +2261,7 @@ The platform understands the following policy categories (specifiable as the
|
|||||||
|
|
||||||
* `action`: Determines whether or not a given action is allowable. The candidate
|
* `action`: Determines whether or not a given action is allowable. The candidate
|
||||||
argument here is an Action; the context is its action context object.
|
argument here is an Action; the context is its action context object.
|
||||||
* `composition`: Determines whether or not domain objects of a given type are
|
* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`).
|
||||||
allowed to contain domain objects of another type. The candidate argument here
|
|
||||||
is the container's `Type`; the context argument is the `Type` of the object to be
|
|
||||||
contained.
|
|
||||||
* `view`: Determines whether or not a view is applicable for a domain object.
|
* `view`: Determines whether or not a view is applicable for a domain object.
|
||||||
The candidate argument is the view's extension definition; the context argument
|
The candidate argument is the view's extension definition; the context argument
|
||||||
is the `DomainObject` to be viewed.
|
is the `DomainObject` to be viewed.
|
||||||
|
@ -54,8 +54,7 @@ define(
|
|||||||
AddActionProvider.prototype.getActions = function (actionContext) {
|
AddActionProvider.prototype.getActions = function (actionContext) {
|
||||||
var context = actionContext || {},
|
var context = actionContext || {},
|
||||||
key = context.key,
|
key = context.key,
|
||||||
destination = context.domainObject,
|
destination = context.domainObject;
|
||||||
self = this;
|
|
||||||
|
|
||||||
// We only provide Add actions, and we need a
|
// We only provide Add actions, and we need a
|
||||||
// domain object to serve as the container for the
|
// domain object to serve as the container for the
|
||||||
@ -66,18 +65,16 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Introduce one create action per type
|
// Introduce one create action per type
|
||||||
return this.typeService.listTypes().filter(function (type) {
|
return ['timeline', 'activity'].map(function (type) {
|
||||||
return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type);
|
|
||||||
}).map(function (type) {
|
|
||||||
return new AddAction(
|
return new AddAction(
|
||||||
type,
|
this.typeService.getType(type),
|
||||||
destination,
|
destination,
|
||||||
context,
|
context,
|
||||||
self.$q,
|
this.$q,
|
||||||
self.dialogService,
|
this.dialogService,
|
||||||
self.policyService
|
this.policyService
|
||||||
);
|
);
|
||||||
});
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
return AddActionProvider;
|
return AddActionProvider;
|
||||||
|
@ -56,16 +56,16 @@ define(
|
|||||||
*/
|
*/
|
||||||
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
||||||
var sections = [],
|
var sections = [],
|
||||||
type = this.type,
|
domainObject = this.domainObject,
|
||||||
policyService = this.policyService;
|
policyService = this.policyService;
|
||||||
|
|
||||||
function validateLocation(locatingObject) {
|
function validateLocation(parent) {
|
||||||
var locatingType = locatingObject &&
|
var parentType = parent &&
|
||||||
locatingObject.getCapability('type');
|
parent.getCapability('type');
|
||||||
return locatingType && policyService.allow(
|
return parentType && policyService.allow(
|
||||||
"composition",
|
"composition",
|
||||||
locatingType,
|
parentType,
|
||||||
type
|
domainObject
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,7 @@ define(
|
|||||||
var mockTypeService,
|
var mockTypeService,
|
||||||
mockDialogService,
|
mockDialogService,
|
||||||
mockPolicyService,
|
mockPolicyService,
|
||||||
mockCreationPolicy,
|
mockTypeMap,
|
||||||
mockCompositionPolicy,
|
|
||||||
mockPolicyMap = {},
|
|
||||||
mockTypes,
|
mockTypes,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockQ,
|
mockQ,
|
||||||
@ -55,49 +53,33 @@ define(
|
|||||||
);
|
);
|
||||||
mockType.hasFeature.andReturn(true);
|
mockType.hasFeature.andReturn(true);
|
||||||
mockType.getName.andReturn(name);
|
mockType.getName.andReturn(name);
|
||||||
|
mockType.getKey.andReturn(name);
|
||||||
return mockType;
|
return mockType;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockTypeService = jasmine.createSpyObj(
|
mockTypeService = jasmine.createSpyObj(
|
||||||
"typeService",
|
"typeService",
|
||||||
["listTypes"]
|
["getType"]
|
||||||
);
|
|
||||||
mockDialogService = jasmine.createSpyObj(
|
|
||||||
"dialogService",
|
|
||||||
["getUserInput"]
|
|
||||||
);
|
|
||||||
mockPolicyService = jasmine.createSpyObj(
|
|
||||||
"policyService",
|
|
||||||
["allow"]
|
|
||||||
);
|
);
|
||||||
|
mockDialogService = {};
|
||||||
|
mockPolicyService = {};
|
||||||
|
mockDomainObject = {};
|
||||||
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockTypes = [
|
||||||
"domainObject",
|
"timeline",
|
||||||
["getCapability"]
|
"activity",
|
||||||
);
|
"other"
|
||||||
|
].map(createMockType);
|
||||||
//Mocking getCapability because AddActionProvider uses the
|
mockTypeMap = {};
|
||||||
// type capability of the destination object.
|
|
||||||
mockDomainObject.getCapability.andReturn({});
|
|
||||||
|
|
||||||
mockTypes = ["A", "B", "C"].map(createMockType);
|
|
||||||
|
|
||||||
mockTypes.forEach(function (type) {
|
mockTypes.forEach(function (type) {
|
||||||
mockPolicyMap[type.getName()] = true;
|
mockTypeMap[type.getKey()] = type;
|
||||||
});
|
});
|
||||||
|
|
||||||
mockCreationPolicy = function (type) {
|
mockTypeService.getType.andCallFake(function (key) {
|
||||||
return mockPolicyMap[type.getName()];
|
return mockTypeMap[key];
|
||||||
};
|
});
|
||||||
|
|
||||||
mockCompositionPolicy = function () {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
mockPolicyService.allow.andReturn(true);
|
|
||||||
|
|
||||||
mockTypeService.listTypes.andReturn(mockTypes);
|
|
||||||
|
|
||||||
provider = new AddActionProvider(
|
provider = new AddActionProvider(
|
||||||
mockQ,
|
mockQ,
|
||||||
@ -107,29 +89,16 @@ define(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks for creatability", function () {
|
it("provides actions for timeline and activity", function () {
|
||||||
provider.getActions({
|
var actions = provider.getActions({
|
||||||
key: "add",
|
key: "add",
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
});
|
});
|
||||||
|
expect(actions.length).toBe(2);
|
||||||
|
expect(actions[0].metadata.type).toBe('timeline');
|
||||||
|
expect(actions[1].metadata.type).toBe('activity');
|
||||||
|
|
||||||
// Make sure it was creation which was used to check
|
// Make sure it was creation which was used to check
|
||||||
expect(mockPolicyService.allow)
|
|
||||||
.toHaveBeenCalledWith("creation", mockTypes[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("checks for composability of type", function () {
|
|
||||||
provider.getActions({
|
|
||||||
key: "add",
|
|
||||||
domainObject: mockDomainObject
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
|
||||||
"composition",
|
|
||||||
jasmine.any(Object),
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('type');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ define(
|
|||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
||||||
'composition',
|
'composition',
|
||||||
mockOtherType,
|
mockOtherType,
|
||||||
mockType
|
mockDomainObject
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,9 +40,6 @@ define([
|
|||||||
{
|
{
|
||||||
"category": "composition",
|
"category": "composition",
|
||||||
"implementation": CompositionPolicy,
|
"implementation": CompositionPolicy,
|
||||||
"depends": [
|
|
||||||
"$injector"
|
|
||||||
],
|
|
||||||
"message": "Objects of this type cannot contain objects of that type."
|
"message": "Objects of this type cannot contain objects of that type."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT 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 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a table indicating which types are expected to expose
|
|
||||||
* which capabilities. This supports composition policy (rules
|
|
||||||
* for which objects can contain which other objects) which
|
|
||||||
* sometimes is determined based on the presence of capabilities.
|
|
||||||
* @constructor
|
|
||||||
* @memberof platform/containment
|
|
||||||
*/
|
|
||||||
function CapabilityTable(typeService, capabilityService) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Build an initial model for a type
|
|
||||||
function buildModel(type) {
|
|
||||||
var model = Object.create(type.getInitialModel() || {});
|
|
||||||
model.type = type.getKey();
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get capabilities expected for this type
|
|
||||||
function getCapabilities(type) {
|
|
||||||
return capabilityService.getCapabilities(buildModel(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the lookup table for this type's capabilities
|
|
||||||
function addToTable(type) {
|
|
||||||
var typeKey = type.getKey();
|
|
||||||
Object.keys(getCapabilities(type)).forEach(function (key) {
|
|
||||||
self.table[key] = self.table[key] || {};
|
|
||||||
self.table[key][typeKey] = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the table
|
|
||||||
this.table = {};
|
|
||||||
(typeService.listTypes() || []).forEach(addToTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a type is expected to expose a specific capability.
|
|
||||||
* @param {string} typeKey the type identifier
|
|
||||||
* @param {string} capabilityKey the capability identifier
|
|
||||||
* @returns {boolean} true if expected to be exposed
|
|
||||||
*/
|
|
||||||
CapabilityTable.prototype.hasCapability = function (typeKey, capabilityKey) {
|
|
||||||
return (this.table[capabilityKey] || {})[typeKey];
|
|
||||||
};
|
|
||||||
|
|
||||||
return CapabilityTable;
|
|
||||||
}
|
|
||||||
);
|
|
@ -45,9 +45,7 @@ define(
|
|||||||
ComposeActionPolicy.prototype.allowComposition = function (containerObject, selectedObject) {
|
ComposeActionPolicy.prototype.allowComposition = function (containerObject, selectedObject) {
|
||||||
// Get the object types involved in the compose action
|
// Get the object types involved in the compose action
|
||||||
var containerType = containerObject &&
|
var containerType = containerObject &&
|
||||||
containerObject.getCapability('type'),
|
containerObject.getCapability('type');
|
||||||
selectedType = selectedObject &&
|
|
||||||
selectedObject.getCapability('type');
|
|
||||||
|
|
||||||
// Get a reference to the policy service if needed...
|
// Get a reference to the policy service if needed...
|
||||||
this.policyService = this.policyService || this.getPolicyService();
|
this.policyService = this.policyService || this.getPolicyService();
|
||||||
@ -57,7 +55,7 @@ define(
|
|||||||
this.policyService.allow(
|
this.policyService.allow(
|
||||||
'composition',
|
'composition',
|
||||||
containerType,
|
containerType,
|
||||||
selectedType
|
selectedObject
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
* @namespace platform/containment
|
* @namespace platform/containment
|
||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
['./ContainmentTable'],
|
[],
|
||||||
function (ContainmentTable) {
|
function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines composition policy as driven by type metadata.
|
* Defines composition policy as driven by type metadata.
|
||||||
@ -35,21 +35,33 @@ define(
|
|||||||
* @memberof platform/containment
|
* @memberof platform/containment
|
||||||
* @implements {Policy.<Type, Type>}
|
* @implements {Policy.<Type, Type>}
|
||||||
*/
|
*/
|
||||||
function CompositionPolicy($injector) {
|
function CompositionPolicy() {
|
||||||
// We're really just wrapping the containment table and rephrasing
|
|
||||||
// it as a policy decision.
|
|
||||||
var table;
|
|
||||||
|
|
||||||
this.getTable = function () {
|
|
||||||
return (table = table || new ContainmentTable(
|
|
||||||
$injector.get('typeService'),
|
|
||||||
$injector.get('capabilityService')
|
|
||||||
));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionPolicy.prototype.allow = function (candidate, context) {
|
CompositionPolicy.prototype.allow = function (parentType, child) {
|
||||||
return this.getTable().canContain(candidate, context);
|
var parentDef = parentType.getDefinition();
|
||||||
|
|
||||||
|
// A parent without containment rules can contain anything.
|
||||||
|
if (!parentDef.contains) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any containment rule matches context type, the candidate
|
||||||
|
// can contain this type.
|
||||||
|
return parentDef.contains.some(function (c) {
|
||||||
|
// Simple containment rules are supported typeKeys.
|
||||||
|
if (typeof c === 'string') {
|
||||||
|
return c === child.getCapability('type').getKey();
|
||||||
|
}
|
||||||
|
// More complicated rules require context to have all specified
|
||||||
|
// capabilities.
|
||||||
|
if (!Array.isArray(c.has)) {
|
||||||
|
c.has = [c.has];
|
||||||
|
}
|
||||||
|
return c.has.every(function (capability) {
|
||||||
|
return child.hasCapability(capability);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return CompositionPolicy;
|
return CompositionPolicy;
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT 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 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
['./CapabilityTable'],
|
|
||||||
function (CapabilityTable) {
|
|
||||||
|
|
||||||
// Symbolic value for the type table for cases when any type
|
|
||||||
// is allowed to be contained.
|
|
||||||
var ANY = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supports composition policy by maintaining a table of
|
|
||||||
* domain object types, to determine if they can contain
|
|
||||||
* other domain object types. This is determined at application
|
|
||||||
* start time (plug-in support means this cannot be determined
|
|
||||||
* prior to that, but we don't want to redo these calculations
|
|
||||||
* every time policy is checked.)
|
|
||||||
* @constructor
|
|
||||||
* @memberof platform/containment
|
|
||||||
*/
|
|
||||||
function ContainmentTable(typeService, capabilityService) {
|
|
||||||
var self = this,
|
|
||||||
types = typeService.listTypes(),
|
|
||||||
capabilityTable = new CapabilityTable(typeService, capabilityService);
|
|
||||||
|
|
||||||
// Add types which have all these capabilities to the set
|
|
||||||
// of allowed types
|
|
||||||
function addToSetByCapability(set, has) {
|
|
||||||
has = Array.isArray(has) ? has : [has];
|
|
||||||
types.forEach(function (type) {
|
|
||||||
var typeKey = type.getKey();
|
|
||||||
set[typeKey] = has.map(function (capabilityKey) {
|
|
||||||
return capabilityTable.hasCapability(typeKey, capabilityKey);
|
|
||||||
}).reduce(function (a, b) {
|
|
||||||
return a && b;
|
|
||||||
}, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add this type (or type description) to the set of allowed types
|
|
||||||
function addToSet(set, type) {
|
|
||||||
// Is this a simple case of an explicit type identifier?
|
|
||||||
if (typeof type === 'string') {
|
|
||||||
// If so, add it to the set of allowed types
|
|
||||||
set[type] = true;
|
|
||||||
} else {
|
|
||||||
// Otherwise, populate that set based on capabilities
|
|
||||||
addToSetByCapability(set, (type || {}).has || []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the lookup table for this type
|
|
||||||
function addToTable(type) {
|
|
||||||
var key = type.getKey(),
|
|
||||||
definition = type.getDefinition() || {},
|
|
||||||
contains = definition.contains;
|
|
||||||
|
|
||||||
// Check for defined containment restrictions
|
|
||||||
if (contains === undefined) {
|
|
||||||
// If not, accept anything
|
|
||||||
self.table[key] = ANY;
|
|
||||||
} else {
|
|
||||||
// Start with an empty set...
|
|
||||||
self.table[key] = {};
|
|
||||||
// ...cast accepted types to array if necessary...
|
|
||||||
contains = Array.isArray(contains) ? contains : [contains];
|
|
||||||
// ...and add all containment rules to that set
|
|
||||||
contains.forEach(function (c) {
|
|
||||||
addToSet(self.table[key], c);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the table
|
|
||||||
this.table = {};
|
|
||||||
types.forEach(addToTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if domain objects of one type can contain domain
|
|
||||||
* objects of another type.
|
|
||||||
* @param {Type} containerType type of the containing domain object
|
|
||||||
* @param {Type} containedType type of the domain object
|
|
||||||
* to be contained
|
|
||||||
* @returns {boolean} true if allowable
|
|
||||||
*/
|
|
||||||
ContainmentTable.prototype.canContain = function (containerType, containedType) {
|
|
||||||
var set = this.table[containerType.getKey()] || {};
|
|
||||||
// Recognize either the symbolic value for "can contain
|
|
||||||
// anything", or lookup the specific type from the set.
|
|
||||||
return (set === ANY) || set[containedType.getKey()];
|
|
||||||
};
|
|
||||||
|
|
||||||
return ContainmentTable;
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,85 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT 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 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
["../src/CapabilityTable"],
|
|
||||||
function (CapabilityTable) {
|
|
||||||
describe("Composition policy's capability table", function () {
|
|
||||||
var mockTypeService,
|
|
||||||
mockCapabilityService,
|
|
||||||
mockTypes,
|
|
||||||
table;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockTypeService = jasmine.createSpyObj(
|
|
||||||
'typeService',
|
|
||||||
['listTypes']
|
|
||||||
);
|
|
||||||
mockCapabilityService = jasmine.createSpyObj(
|
|
||||||
'capabilityService',
|
|
||||||
['getCapabilities']
|
|
||||||
);
|
|
||||||
// Both types can only contain b, let's say
|
|
||||||
mockTypes = ['a', 'b'].map(function (type) {
|
|
||||||
var mockType = jasmine.createSpyObj(
|
|
||||||
'type-' + type,
|
|
||||||
['getKey', 'getDefinition', 'getInitialModel']
|
|
||||||
);
|
|
||||||
mockType.getKey.andReturn(type);
|
|
||||||
// Return a model to drive apparent capabilities
|
|
||||||
mockType.getInitialModel.andReturn({ id: type });
|
|
||||||
return mockType;
|
|
||||||
});
|
|
||||||
|
|
||||||
mockTypeService.listTypes.andReturn(mockTypes);
|
|
||||||
mockCapabilityService.getCapabilities.andCallFake(function (model) {
|
|
||||||
var capabilities = {};
|
|
||||||
capabilities[model.id + '-capability'] = true;
|
|
||||||
return capabilities;
|
|
||||||
});
|
|
||||||
|
|
||||||
table = new CapabilityTable(
|
|
||||||
mockTypeService,
|
|
||||||
mockCapabilityService
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("provides for lookup of capabilities by type", function () {
|
|
||||||
// Based on initial model, should report the presence
|
|
||||||
// of particular capabilities - suffixed above with -capability
|
|
||||||
expect(table.hasCapability('a', 'a-capability'))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(table.hasCapability('a', 'b-capability'))
|
|
||||||
.toBeFalsy();
|
|
||||||
expect(table.hasCapability('a', 'c-capability'))
|
|
||||||
.toBeFalsy();
|
|
||||||
expect(table.hasCapability('b', 'a-capability'))
|
|
||||||
.toBeFalsy();
|
|
||||||
expect(table.hasCapability('b', 'b-capability'))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(table.hasCapability('b', 'c-capability'))
|
|
||||||
.toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -79,7 +79,7 @@ define(
|
|||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
||||||
'composition',
|
'composition',
|
||||||
mockTypes[0],
|
mockTypes[0],
|
||||||
mockTypes[1]
|
mockDomainObjects[1]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,60 +24,94 @@ define(
|
|||||||
["../src/CompositionPolicy"],
|
["../src/CompositionPolicy"],
|
||||||
function (CompositionPolicy) {
|
function (CompositionPolicy) {
|
||||||
describe("Composition policy", function () {
|
describe("Composition policy", function () {
|
||||||
var mockInjector,
|
var typeA,
|
||||||
mockTypeService,
|
typeB,
|
||||||
mockCapabilityService,
|
typeC,
|
||||||
mockTypes,
|
mockChildObject,
|
||||||
policy;
|
policy;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockInjector = jasmine.createSpyObj('$injector', ['get']);
|
typeA = jasmine.createSpyObj(
|
||||||
mockTypeService = jasmine.createSpyObj(
|
'type A-- the particular kind',
|
||||||
'typeService',
|
['getKey', 'getDefinition']
|
||||||
['listTypes']
|
|
||||||
);
|
);
|
||||||
mockCapabilityService = jasmine.createSpyObj(
|
typeA.getKey.andReturn('a');
|
||||||
'capabilityService',
|
typeA.getDefinition.andReturn({
|
||||||
['getCapabilities']
|
contains: ['a']
|
||||||
);
|
|
||||||
// Both types can only contain b, let's say
|
|
||||||
mockTypes = ['a', 'b'].map(function (type) {
|
|
||||||
var mockType = jasmine.createSpyObj(
|
|
||||||
'type-' + type,
|
|
||||||
['getKey', 'getDefinition', 'getInitialModel']
|
|
||||||
);
|
|
||||||
mockType.getKey.andReturn(type);
|
|
||||||
mockType.getDefinition.andReturn({
|
|
||||||
contains: ['b']
|
|
||||||
});
|
|
||||||
mockType.getInitialModel.andReturn({});
|
|
||||||
return mockType;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
mockInjector.get.andCallFake(function (name) {
|
|
||||||
return {
|
typeB = jasmine.createSpyObj(
|
||||||
typeService: mockTypeService,
|
'type B-- anything goes',
|
||||||
capabilityService: mockCapabilityService
|
['getKey', 'getDefinition']
|
||||||
}[name];
|
);
|
||||||
|
typeB.getKey.andReturn('b');
|
||||||
|
typeB.getDefinition.andReturn({
|
||||||
|
contains: ['a', 'b']
|
||||||
});
|
});
|
||||||
|
|
||||||
mockTypeService.listTypes.andReturn(mockTypes);
|
typeC = jasmine.createSpyObj(
|
||||||
mockCapabilityService.getCapabilities.andReturn({});
|
'type C-- distinguishing and interested in telemetry',
|
||||||
|
['getKey', 'getDefinition']
|
||||||
|
);
|
||||||
|
typeC.getKey.andReturn('c');
|
||||||
|
typeC.getDefinition.andReturn({
|
||||||
|
contains: [{has: 'telemetry'}]
|
||||||
|
});
|
||||||
|
|
||||||
policy = new CompositionPolicy(mockInjector);
|
mockChildObject = jasmine.createSpyObj(
|
||||||
|
'childObject',
|
||||||
|
['getCapability', 'hasCapability']
|
||||||
|
);
|
||||||
|
|
||||||
|
policy = new CompositionPolicy();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test basic composition policy here; test more closely at
|
describe('enforces simple containment rules', function () {
|
||||||
// the unit level in ContainmentTable for 'has' support, et al
|
|
||||||
it("enforces containment rules defined by types", function () {
|
it('allows when type matches', function () {
|
||||||
expect(policy.allow(mockTypes[0], mockTypes[1]))
|
mockChildObject.getCapability.andReturn(typeA);
|
||||||
.toBeTruthy();
|
expect(policy.allow(typeA, mockChildObject))
|
||||||
expect(policy.allow(mockTypes[1], mockTypes[1]))
|
.toBeTruthy();
|
||||||
.toBeTruthy();
|
|
||||||
expect(policy.allow(mockTypes[1], mockTypes[0]))
|
expect(policy.allow(typeB, mockChildObject))
|
||||||
.toBeFalsy();
|
.toBeTruthy();
|
||||||
expect(policy.allow(mockTypes[0], mockTypes[0]))
|
|
||||||
.toBeFalsy();
|
mockChildObject.getCapability.andReturn(typeB);
|
||||||
|
expect(policy.allow(typeB, mockChildObject))
|
||||||
|
.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('disallows when type doesn\'t match', function () {
|
||||||
|
|
||||||
|
mockChildObject.getCapability.andReturn(typeB);
|
||||||
|
expect(policy.allow(typeA, mockChildObject))
|
||||||
|
.toBeFalsy();
|
||||||
|
|
||||||
|
mockChildObject.getCapability.andReturn(typeC);
|
||||||
|
expect(policy.allow(typeA, mockChildObject))
|
||||||
|
.toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('enforces capability-based containment rules', function () {
|
||||||
|
it('allows when object has capability', function () {
|
||||||
|
mockChildObject.hasCapability.andReturn(true);
|
||||||
|
expect(policy.allow(typeC, mockChildObject))
|
||||||
|
.toBeTruthy();
|
||||||
|
expect(mockChildObject.hasCapability)
|
||||||
|
.toHaveBeenCalledWith('telemetry');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips when object doesn\'t have capability', function () {
|
||||||
|
mockChildObject.hasCapability.andReturn(false);
|
||||||
|
expect(policy.allow(typeC, mockChildObject))
|
||||||
|
.toBeFalsy();
|
||||||
|
expect(mockChildObject.hasCapability)
|
||||||
|
.toHaveBeenCalledWith('telemetry');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT 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 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
["../src/ContainmentTable"],
|
|
||||||
function (ContainmentTable) {
|
|
||||||
describe("Composition policy's containment table", function () {
|
|
||||||
var mockTypeService,
|
|
||||||
mockCapabilityService,
|
|
||||||
mockTypes,
|
|
||||||
table;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockTypeService = jasmine.createSpyObj(
|
|
||||||
'typeService',
|
|
||||||
['listTypes']
|
|
||||||
);
|
|
||||||
mockCapabilityService = jasmine.createSpyObj(
|
|
||||||
'capabilityService',
|
|
||||||
['getCapabilities']
|
|
||||||
);
|
|
||||||
// Both types can only contain b, let's say
|
|
||||||
mockTypes = ['a', 'b', 'c'].map(function (type, index) {
|
|
||||||
var mockType = jasmine.createSpyObj(
|
|
||||||
'type-' + type,
|
|
||||||
['getKey', 'getDefinition', 'getInitialModel']
|
|
||||||
);
|
|
||||||
mockType.getKey.andReturn(type);
|
|
||||||
mockType.getDefinition.andReturn({
|
|
||||||
// First two contain objects with capability 'b';
|
|
||||||
// third one defines no containership rules
|
|
||||||
contains: (index < 2) ? [{ has: 'b' }] : undefined
|
|
||||||
});
|
|
||||||
// Return a model to drive apparent capabilities
|
|
||||||
mockType.getInitialModel.andReturn({ id: type });
|
|
||||||
return mockType;
|
|
||||||
});
|
|
||||||
|
|
||||||
mockTypeService.listTypes.andReturn(mockTypes);
|
|
||||||
mockCapabilityService.getCapabilities.andCallFake(function (model) {
|
|
||||||
var capabilities = {};
|
|
||||||
capabilities[model.id] = true;
|
|
||||||
return capabilities;
|
|
||||||
});
|
|
||||||
|
|
||||||
table = new ContainmentTable(
|
|
||||||
mockTypeService,
|
|
||||||
mockCapabilityService
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// The plain type case is tested in CompositionPolicySpec,
|
|
||||||
// so just test for special syntax ('has', or no contains rules) here
|
|
||||||
it("enforces 'has' containment rules related to capabilities", function () {
|
|
||||||
expect(table.canContain(mockTypes[0], mockTypes[1]))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(table.canContain(mockTypes[1], mockTypes[1]))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(table.canContain(mockTypes[1], mockTypes[0]))
|
|
||||||
.toBeFalsy();
|
|
||||||
expect(table.canContain(mockTypes[0], mockTypes[0]))
|
|
||||||
.toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows anything when no containership rules are defined", function () {
|
|
||||||
expect(table.canContain(mockTypes[2], mockTypes[0]))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(table.canContain(mockTypes[2], mockTypes[1]))
|
|
||||||
.toBeTruthy();
|
|
||||||
expect(table.canContain(mockTypes[2], mockTypes[2]))
|
|
||||||
.toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -48,7 +48,7 @@ define(
|
|||||||
return this.policyService.allow(
|
return this.policyService.allow(
|
||||||
"composition",
|
"composition",
|
||||||
parentCandidate.getCapability('type'),
|
parentCandidate.getCapability('type'),
|
||||||
object.getCapability('type')
|
object
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ define(
|
|||||||
return this.policyService.allow(
|
return this.policyService.allow(
|
||||||
"composition",
|
"composition",
|
||||||
parentCandidate.getCapability('type'),
|
parentCandidate.getCapability('type'),
|
||||||
object.getCapability('type')
|
object
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ define(
|
|||||||
return this.policyService.allow(
|
return this.policyService.allow(
|
||||||
"composition",
|
"composition",
|
||||||
parentCandidate.getCapability('type'),
|
parentCandidate.getCapability('type'),
|
||||||
object.getCapability('type')
|
object
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ define(
|
|||||||
expect(policyService.allow).toHaveBeenCalledWith(
|
expect(policyService.allow).toHaveBeenCalledWith(
|
||||||
"composition",
|
"composition",
|
||||||
parentCandidate.capabilities.type,
|
parentCandidate.capabilities.type,
|
||||||
object.capabilities.type
|
object
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ define(
|
|||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
||||||
"composition",
|
"composition",
|
||||||
parentCandidate.capabilities.type,
|
parentCandidate.capabilities.type,
|
||||||
object.capabilities.type
|
object
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ define(
|
|||||||
expect(policyService.allow).toHaveBeenCalledWith(
|
expect(policyService.allow).toHaveBeenCalledWith(
|
||||||
"composition",
|
"composition",
|
||||||
parentCandidate.capabilities.type,
|
parentCandidate.capabilities.type,
|
||||||
object.capabilities.type
|
object
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,13 +34,14 @@ define(
|
|||||||
function LayoutCompositionPolicy() {
|
function LayoutCompositionPolicy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutCompositionPolicy.prototype.allow = function (candidate, context) {
|
LayoutCompositionPolicy.prototype.allow = function (parentType, child) {
|
||||||
var isFolderInLayout =
|
if (parentType.instanceOf('layout') &&
|
||||||
candidate &&
|
child.getCapability('type').instanceOf('folder')) {
|
||||||
context &&
|
|
||||||
candidate.instanceOf('layout') &&
|
return false;
|
||||||
context.instanceOf('folder');
|
}
|
||||||
return !isFolderInLayout;
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
return LayoutCompositionPolicy;
|
return LayoutCompositionPolicy;
|
||||||
|
@ -24,18 +24,25 @@ define(
|
|||||||
["../src/LayoutCompositionPolicy"],
|
["../src/LayoutCompositionPolicy"],
|
||||||
function (LayoutCompositionPolicy) {
|
function (LayoutCompositionPolicy) {
|
||||||
describe("Layout's composition policy", function () {
|
describe("Layout's composition policy", function () {
|
||||||
var mockCandidate,
|
var mockChild,
|
||||||
|
mockCandidate,
|
||||||
mockContext,
|
mockContext,
|
||||||
candidateType,
|
candidateType,
|
||||||
contextType,
|
contextType,
|
||||||
policy;
|
policy;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
mockChild = jasmine.createSpyObj(
|
||||||
|
'childObject',
|
||||||
|
['getCapability']
|
||||||
|
);
|
||||||
mockCandidate =
|
mockCandidate =
|
||||||
jasmine.createSpyObj('candidateType', ['instanceOf']);
|
jasmine.createSpyObj('candidateType', ['instanceOf']);
|
||||||
mockContext =
|
mockContext =
|
||||||
jasmine.createSpyObj('contextType', ['instanceOf']);
|
jasmine.createSpyObj('contextType', ['instanceOf']);
|
||||||
|
|
||||||
|
mockChild.getCapability.andReturn(mockContext);
|
||||||
|
|
||||||
mockCandidate.instanceOf.andCallFake(function (t) {
|
mockCandidate.instanceOf.andCallFake(function (t) {
|
||||||
return t === candidateType;
|
return t === candidateType;
|
||||||
});
|
});
|
||||||
@ -49,19 +56,19 @@ define(
|
|||||||
it("disallows folders in layouts", function () {
|
it("disallows folders in layouts", function () {
|
||||||
candidateType = 'layout';
|
candidateType = 'layout';
|
||||||
contextType = 'folder';
|
contextType = 'folder';
|
||||||
expect(policy.allow(mockCandidate, mockContext)).toBe(false);
|
expect(policy.allow(mockCandidate, mockChild)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not disallow folders elsewhere", function () {
|
it("does not disallow folders elsewhere", function () {
|
||||||
candidateType = 'nonlayout';
|
candidateType = 'nonlayout';
|
||||||
contextType = 'folder';
|
contextType = 'folder';
|
||||||
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
|
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows things other than folders in layouts", function () {
|
it("allows things other than folders in layouts", function () {
|
||||||
candidateType = 'layout';
|
candidateType = 'layout';
|
||||||
contextType = 'nonfolder';
|
contextType = 'nonfolder';
|
||||||
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
|
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -26,15 +26,14 @@ define([], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AdapterCompositionPolicy.prototype.allow = function (
|
AdapterCompositionPolicy.prototype.allow = function (
|
||||||
containerType,
|
parentType,
|
||||||
childType
|
child
|
||||||
) {
|
) {
|
||||||
var containerObject = containerType.getInitialModel();
|
var container = parentType.getInitialModel();
|
||||||
var childObject = childType.getInitialModel();
|
|
||||||
|
|
||||||
return this.openmct.composition.checkPolicy(
|
return this.openmct.composition.checkPolicy(
|
||||||
containerObject,
|
container,
|
||||||
childObject
|
child
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user