Merge pull request #1448 from nasa/update-composition-policy

Update composition policy
This commit is contained in:
Andrew Henry
2017-02-21 17:18:59 -08:00
committed by GitHub
23 changed files with 175 additions and 538 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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
); );
} }

View File

@ -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');
}); });
}); });
} }

View File

@ -175,7 +175,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith( expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition', 'composition',
mockOtherType, mockOtherType,
mockType mockDomainObject
); );
}); });

View File

@ -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."
}, },
{ {

View File

@ -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;
}
);

View File

@ -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
); );
}; };

View File

@ -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;

View File

@ -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;
}
);

View File

@ -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();
});
});
}
);

View File

@ -79,7 +79,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith( expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition', 'composition',
mockTypes[0], mockTypes[0],
mockTypes[1] mockDomainObjects[1]
); );
}); });

View File

@ -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');
});
}); });
}); });

View File

@ -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();
});
});
}
);

View File

@ -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
); );
}; };

View File

@ -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
); );
}; };

View File

@ -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
); );
}; };

View File

@ -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
); );
}); });

View File

@ -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
); );
}); });

View File

@ -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
); );
}); });

View File

@ -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;

View File

@ -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);
}); });
}); });

View File

@ -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
); );
}; };