mirror of
https://github.com/nasa/openmct.git
synced 2025-02-21 09:52:04 +00:00
Composition policy takes child instance
The composition policy now takes a child instance instead of the child type, as in all cases we have access to the child object. This allows new-style objects to be contained by old-style objects. Updated all composition policies to use standardized argument names instead of `context` and `candidate`; this makes it easier to understand. Updated AddActionProvider to hardcode the object types supported.
This commit is contained in:
parent
46c7399867
commit
65325b90fd
@ -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
|
||||
argument here is an Action; the context is its action context object.
|
||||
* `composition`: Determines whether or not domain objects of a given type are
|
||||
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.
|
||||
* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`).
|
||||
* `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
|
||||
is the `DomainObject` to be viewed.
|
||||
|
@ -66,9 +66,7 @@ define(
|
||||
}
|
||||
|
||||
// Introduce one create action per type
|
||||
return this.typeService.listTypes().filter(function (type) {
|
||||
return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type);
|
||||
}).map(function (type) {
|
||||
['timeline', 'activity'].map(function (type) {
|
||||
return new AddAction(
|
||||
type,
|
||||
destination,
|
||||
|
@ -56,16 +56,16 @@ define(
|
||||
*/
|
||||
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
||||
var sections = [],
|
||||
type = this.type,
|
||||
domainObject = this.domainObject,
|
||||
policyService = this.policyService;
|
||||
|
||||
function validateLocation(locatingObject) {
|
||||
var locatingType = locatingObject &&
|
||||
locatingObject.getCapability('type');
|
||||
return locatingType && policyService.allow(
|
||||
function validateLocation(parent) {
|
||||
var parentType = parent &&
|
||||
parent.getCapability('type');
|
||||
return parentType && policyService.allow(
|
||||
"composition",
|
||||
locatingType,
|
||||
type
|
||||
parentType,
|
||||
domainObject
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,6 @@ define([
|
||||
{
|
||||
"category": "composition",
|
||||
"implementation": CompositionPolicy,
|
||||
"depends": [
|
||||
"$injector"
|
||||
],
|
||||
"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) {
|
||||
// Get the object types involved in the compose action
|
||||
var containerType = containerObject &&
|
||||
containerObject.getCapability('type'),
|
||||
selectedType = selectedObject &&
|
||||
selectedObject.getCapability('type');
|
||||
containerObject.getCapability('type');
|
||||
|
||||
// Get a reference to the policy service if needed...
|
||||
this.policyService = this.policyService || this.getPolicyService();
|
||||
@ -57,7 +55,7 @@ define(
|
||||
this.policyService.allow(
|
||||
'composition',
|
||||
containerType,
|
||||
selectedType
|
||||
selectedObject
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -26,8 +26,8 @@
|
||||
* @namespace platform/containment
|
||||
*/
|
||||
define(
|
||||
['./ContainmentTable'],
|
||||
function (ContainmentTable) {
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Defines composition policy as driven by type metadata.
|
||||
@ -35,21 +35,35 @@ define(
|
||||
* @memberof platform/containment
|
||||
* @implements {Policy.<Type, Type>}
|
||||
*/
|
||||
function CompositionPolicy($injector) {
|
||||
// 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')
|
||||
));
|
||||
};
|
||||
function CompositionPolicy() {
|
||||
}
|
||||
|
||||
CompositionPolicy.prototype.allow = function (candidate, context) {
|
||||
return this.getTable().canContain(candidate, context);
|
||||
CompositionPolicy.prototype.allow = function (parentType, child) {
|
||||
var childType = child.getCapability('type');
|
||||
var childTypeKey = childType.getKey();
|
||||
var parentDef = parent.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 === childTypeKey;
|
||||
}
|
||||
// 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;
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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(
|
||||
"composition",
|
||||
parentCandidate.getCapability('type'),
|
||||
object.getCapability('type')
|
||||
object
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ define(
|
||||
return this.policyService.allow(
|
||||
"composition",
|
||||
parentCandidate.getCapability('type'),
|
||||
object.getCapability('type')
|
||||
object
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -56,7 +56,7 @@ define(
|
||||
return this.policyService.allow(
|
||||
"composition",
|
||||
parentCandidate.getCapability('type'),
|
||||
object.getCapability('type')
|
||||
object
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -34,13 +34,14 @@ define(
|
||||
function LayoutCompositionPolicy() {
|
||||
}
|
||||
|
||||
LayoutCompositionPolicy.prototype.allow = function (candidate, context) {
|
||||
var isFolderInLayout =
|
||||
candidate &&
|
||||
context &&
|
||||
candidate.instanceOf('layout') &&
|
||||
context.instanceOf('folder');
|
||||
return !isFolderInLayout;
|
||||
LayoutCompositionPolicy.prototype.allow = function (parentType, child) {
|
||||
if (parentType.instanceOf('layout') &&
|
||||
child.getCapability('type').instanceOf('folder')) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return LayoutCompositionPolicy;
|
||||
|
@ -26,15 +26,14 @@ define([], function () {
|
||||
}
|
||||
|
||||
AdapterCompositionPolicy.prototype.allow = function (
|
||||
containerType,
|
||||
childType
|
||||
parentType,
|
||||
child
|
||||
) {
|
||||
var containerObject = containerType.getInitialModel();
|
||||
var childObject = childType.getInitialModel();
|
||||
var container = parentType.getInitialModel();
|
||||
|
||||
return this.openmct.composition.checkPolicy(
|
||||
containerObject,
|
||||
childObject
|
||||
container,
|
||||
child
|
||||
);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user